Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c05a3b8ca7 | ||
|
|
de3db48d47 | ||
|
|
9ea444f365 | ||
|
|
3d85dbcb58 | ||
|
|
a128b473ff | ||
|
|
ee5d40cfee |
747
.golangci.yml
747
.golangci.yml
@@ -1,29 +1,12 @@
|
|||||||
|
version: "2"
|
||||||
run:
|
run:
|
||||||
tests: false
|
go: "1.25"
|
||||||
modules-download-mode: readonly
|
modules-download-mode: readonly
|
||||||
|
tests: false
|
||||||
allow-parallel-runners: true
|
allow-parallel-runners: true
|
||||||
go: '1.24'
|
|
||||||
issues:
|
|
||||||
exclude-dirs:
|
|
||||||
- data
|
|
||||||
- vendor
|
|
||||||
output:
|
|
||||||
sort-results: true
|
|
||||||
linters-settings:
|
|
||||||
gosec:
|
|
||||||
excludes:
|
|
||||||
# Оно не используется для повышения безопасности или каких-то таких секъюрых вещей, оно используется для выборки
|
|
||||||
# произвольного элемента из массива, так что ИДИ НАХУЙ с этой проверкой и не еби мозги липовой безопасностью.
|
|
||||||
- G404 # Insecure random number source (rand)
|
|
||||||
linters:
|
linters:
|
||||||
disable-all: true
|
default: none
|
||||||
enable:
|
enable:
|
||||||
- errcheck
|
|
||||||
- gosimple
|
|
||||||
- govet
|
|
||||||
- ineffassign
|
|
||||||
- staticcheck
|
|
||||||
- unused
|
|
||||||
- asasalint
|
- asasalint
|
||||||
- asciicheck
|
- asciicheck
|
||||||
- bodyclose
|
- bodyclose
|
||||||
@@ -31,25 +14,30 @@ linters:
|
|||||||
- contextcheck
|
- contextcheck
|
||||||
- decorder
|
- decorder
|
||||||
- durationcheck
|
- durationcheck
|
||||||
|
- errcheck
|
||||||
- errchkjson
|
- errchkjson
|
||||||
- errorlint
|
- errorlint
|
||||||
- exhaustive
|
- exhaustive
|
||||||
- exhaustruct
|
- exptostd
|
||||||
- copyloopvar
|
|
||||||
- ginkgolinter
|
- ginkgolinter
|
||||||
- gocheckcompilerdirectives
|
- gocheckcompilerdirectives
|
||||||
- gocritic
|
- gocritic
|
||||||
|
- godoclint
|
||||||
- godot
|
- godot
|
||||||
- err113
|
- godox
|
||||||
- gofmt
|
- goprintffuncname
|
||||||
- goimports
|
|
||||||
- gosec
|
- gosec
|
||||||
- gosmopolitan
|
- gosmopolitan
|
||||||
|
- govet
|
||||||
- grouper
|
- grouper
|
||||||
- importas
|
- importas
|
||||||
|
- ineffassign
|
||||||
|
- intrange
|
||||||
- ireturn
|
- ireturn
|
||||||
- makezero
|
- makezero
|
||||||
- mirror
|
- mirror
|
||||||
|
- misspell
|
||||||
|
- modernize
|
||||||
- nakedret
|
- nakedret
|
||||||
- nilerr
|
- nilerr
|
||||||
- nilnil
|
- nilnil
|
||||||
@@ -57,13 +45,714 @@ linters:
|
|||||||
- noctx
|
- noctx
|
||||||
- nolintlint
|
- nolintlint
|
||||||
- nonamedreturns
|
- nonamedreturns
|
||||||
|
- perfsprint
|
||||||
- predeclared
|
- predeclared
|
||||||
- promlinter
|
- promlinter
|
||||||
- reassign
|
- reassign
|
||||||
- revive
|
- revive
|
||||||
- rowserrcheck
|
- rowserrcheck
|
||||||
- stylecheck
|
- staticcheck
|
||||||
- unconvert
|
- unconvert
|
||||||
|
- unused
|
||||||
|
- usestdlibvars
|
||||||
- wastedassign
|
- wastedassign
|
||||||
- whitespace
|
- wsl_v5
|
||||||
- wsl
|
settings:
|
||||||
|
decorder:
|
||||||
|
dec-order:
|
||||||
|
- type
|
||||||
|
- const
|
||||||
|
- var
|
||||||
|
- func
|
||||||
|
gocritic:
|
||||||
|
disable-all: false
|
||||||
|
enable-all: true
|
||||||
|
disabled-checks:
|
||||||
|
- singleCaseSwitch
|
||||||
|
- octalLiteral
|
||||||
|
- regexpSimplify
|
||||||
|
- paramTypeCombine
|
||||||
|
- whyNoLint
|
||||||
|
- hugeParam
|
||||||
|
godoclint:
|
||||||
|
default: all
|
||||||
|
ignore-unexported: false
|
||||||
|
ignore-exported: false
|
||||||
|
include-unexported: true
|
||||||
|
disable:
|
||||||
|
- max-len
|
||||||
|
godot:
|
||||||
|
scope: all
|
||||||
|
exclude:
|
||||||
|
# Exclude todo and fixme comments.
|
||||||
|
- "^fixme:"
|
||||||
|
- "^todo:"
|
||||||
|
period: true
|
||||||
|
capital: false
|
||||||
|
godox:
|
||||||
|
keywords:
|
||||||
|
- FIXME
|
||||||
|
gosec: # go-fucking-sic
|
||||||
|
excludes:
|
||||||
|
# Боже, как же вы затрахали своей крипто-ебучкой, наркоманы хреновы!
|
||||||
|
- G405 # Detect the usage of DES or RC4
|
||||||
|
- G406 # Detect the usage of MD4 or RIPEMD160
|
||||||
|
# Оно не используется для повышения безопасности или каких-то таких секъюрных вещей, оно используется для выборки
|
||||||
|
# произвольного элемента из массива, так что ИДИ НАХУЙ с этой проверкой и не еби мозги липовой безопасностью.
|
||||||
|
- G404 # Insecure random number source (rand)
|
||||||
|
- G501 # Import blocklist: crypto/md5
|
||||||
|
- G502 # Import blocklist: crypto/des
|
||||||
|
- G503 # Import blocklist: crypto/rc4
|
||||||
|
- G504 # Import blocklist: net/http/cgi
|
||||||
|
- G505 # Import blocklist: crypto/sha1
|
||||||
|
- G506 # Import blocklist: golang.org/x/crypto/md4
|
||||||
|
- G507 # Import blocklist: golang.org/x/crypto/ripemd160
|
||||||
|
config:
|
||||||
|
global:
|
||||||
|
audit: true
|
||||||
|
# Ваша сраная паранойя при создании файлов и каталогов мешает нормальной работе сервисов. Группы придуманы для
|
||||||
|
# удобства, группе удобно давать право чтения. Поэтому идите нахуй со своими ограничениями на чтение для
|
||||||
|
# группы, "безопасные" клоуны.
|
||||||
|
G301: "0755"
|
||||||
|
G306: "0644"
|
||||||
|
govet:
|
||||||
|
disable-all: false
|
||||||
|
enable-all: true
|
||||||
|
disable:
|
||||||
|
- shadow
|
||||||
|
- fieldalignment
|
||||||
|
grouper:
|
||||||
|
# Require the use of a single global 'const' declaration only.
|
||||||
|
# Default: false
|
||||||
|
const-require-single-const: true
|
||||||
|
# Require the use of grouped global 'const' declarations.
|
||||||
|
# Default: false
|
||||||
|
const-require-grouping: true
|
||||||
|
# Require the use of a single 'import' declaration only.
|
||||||
|
# Default: false
|
||||||
|
import-require-single-import: true
|
||||||
|
# Require the use of grouped 'import' declarations.
|
||||||
|
# Default: false
|
||||||
|
import-require-grouping: true
|
||||||
|
# Require the use of a single global 'type' declaration only.
|
||||||
|
# Default: false
|
||||||
|
type-require-single-type: true
|
||||||
|
# Require the use of grouped global 'type' declarations.
|
||||||
|
# Default: false
|
||||||
|
type-require-grouping: true
|
||||||
|
# Require the use of a single global 'var' declaration only.
|
||||||
|
# Default: false
|
||||||
|
var-require-single-var: true
|
||||||
|
# Require the use of grouped global 'var' declarations.
|
||||||
|
# Default: false
|
||||||
|
var-require-grouping: true
|
||||||
|
misspell:
|
||||||
|
# Correct spellings using locale preferences for US or UK.
|
||||||
|
# Setting locale to US will correct the British spelling of 'colour' to 'color'.
|
||||||
|
# Default is to use a neutral variety of English.
|
||||||
|
locale: US
|
||||||
|
# Typos to ignore.
|
||||||
|
# Should be in lower case.
|
||||||
|
# Default: []
|
||||||
|
ignore-rules:
|
||||||
|
- someword
|
||||||
|
# Extra word corrections.
|
||||||
|
# `typo` and `correction` should only contain letters.
|
||||||
|
# The words are case-insensitive.
|
||||||
|
# Default: []
|
||||||
|
extra-words:
|
||||||
|
- typo: "iff"
|
||||||
|
correction: "if"
|
||||||
|
- typo: "cancelation"
|
||||||
|
correction: "cancellation"
|
||||||
|
# Mode of the analysis:
|
||||||
|
# - default: checks all the file content.
|
||||||
|
# - restricted: checks only comments.
|
||||||
|
# Default: ""
|
||||||
|
mode: restricted
|
||||||
|
nonamedreturns:
|
||||||
|
# Report named error if it is assigned inside defer.
|
||||||
|
# Default: false
|
||||||
|
report-error-in-defer: true
|
||||||
|
perfsprint:
|
||||||
|
integer-format: true
|
||||||
|
int-conversion: true
|
||||||
|
error-format: true
|
||||||
|
err-error: false
|
||||||
|
errorf: true
|
||||||
|
string-format: true
|
||||||
|
sprintf1: true
|
||||||
|
strconcat: true
|
||||||
|
bool-format: true
|
||||||
|
hex-format: true
|
||||||
|
concat-loop: true
|
||||||
|
loop-other-ops: false
|
||||||
|
revive:
|
||||||
|
enable-all-rules: false
|
||||||
|
staticcheck:
|
||||||
|
# https://staticcheck.dev/docs/configuration/options/#dot_import_whitelist
|
||||||
|
# Default: ["github.com/mmcloughlin/avo/build", "github.com/mmcloughlin/avo/operand", "github.com/mmcloughlin/avo/reg"]
|
||||||
|
dot-import-whitelist:
|
||||||
|
- fmt
|
||||||
|
# https://staticcheck.dev/docs/configuration/options/#initialisms
|
||||||
|
# Default: ["ACL", "API", "ASCII", "CPU", "CSS", "DNS", "EOF", "GUID", "HTML", "HTTP", "HTTPS", "ID", "IP", "JSON", "QPS", "RAM", "RPC", "SLA", "SMTP", "SQL", "SSH", "TCP", "TLS", "TTL", "UDP", "UI", "GID", "UID", "UUID", "URI", "URL", "UTF8", "VM", "XML", "XMPP", "XSRF", "XSS", "SIP", "RTP", "AMQP", "DB", "TS"]
|
||||||
|
initialisms: ["ACL", "API", "ASCII", "CPU", "CSS", "DNS", "EOF", "GUID", "HTML", "HTTP", "HTTPS", "ID", "IP", "JSON", "QPS", "RAM", "RPC", "SLA", "SMTP", "SQL", "SSH", "TCP", "TLS", "TTL", "UDP", "UI", "GID", "UID", "UUID", "URI", "URL", "UTF8", "VM", "XML", "XMPP", "XSRF", "XSS", "SIP", "RTP", "AMQP", "DB", "TS"]
|
||||||
|
# https://staticcheck.dev/docs/configuration/options/#http_status_code_whitelist
|
||||||
|
# Default: ["200", "400", "404", "500"]
|
||||||
|
http-status-code-whitelist: ["200", "400", "404", "500"]
|
||||||
|
# SAxxxx checks in https://staticcheck.dev/docs/configuration/options/#checks
|
||||||
|
# Example (to disable some checks): [ "all", "-SA1000", "-SA1001"]
|
||||||
|
# Run `GL_DEBUG=staticcheck golangci-lint run --enable=staticcheck` to see all available checks and enabled by config checks.
|
||||||
|
# Default: ["all", "-ST1000", "-ST1003", "-ST1016", "-ST1020", "-ST1021", "-ST1022"]
|
||||||
|
checks:
|
||||||
|
# Invalid regular expression.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA1000
|
||||||
|
- SA1000
|
||||||
|
# Invalid template.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA1001
|
||||||
|
- SA1001
|
||||||
|
# Invalid format in 'time.Parse'.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA1002
|
||||||
|
- SA1002
|
||||||
|
# Unsupported argument to functions in 'encoding/binary'.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA1003
|
||||||
|
- SA1003
|
||||||
|
# Suspiciously small untyped constant in 'time.Sleep'.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA1004
|
||||||
|
- SA1004
|
||||||
|
# Invalid first argument to 'exec.Command'.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA1005
|
||||||
|
- SA1005
|
||||||
|
# 'Printf' with dynamic first argument and no further arguments.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA1006
|
||||||
|
- SA1006
|
||||||
|
# Invalid URL in 'net/url.Parse'.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA1007
|
||||||
|
- SA1007
|
||||||
|
# Non-canonical key in 'http.Header' map.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA1008
|
||||||
|
- SA1008
|
||||||
|
# '(*regexp.Regexp).FindAll' called with 'n == 0', which will always return zero results.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA1010
|
||||||
|
- SA1010
|
||||||
|
# Various methods in the "strings" package expect valid UTF-8, but invalid input is provided.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA1011
|
||||||
|
- SA1011
|
||||||
|
# A nil 'context.Context' is being passed to a function, consider using 'context.TODO' instead.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA1012
|
||||||
|
- SA1012
|
||||||
|
# 'io.Seeker.Seek' is being called with the whence constant as the first argument, but it should be the second.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA1013
|
||||||
|
- SA1013
|
||||||
|
# Non-pointer value passed to 'Unmarshal' or 'Decode'.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA1014
|
||||||
|
- SA1014
|
||||||
|
# Using 'time.Tick' in a way that will leak. Consider using 'time.NewTicker', and only use 'time.Tick' in tests, commands and endless functions.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA1015
|
||||||
|
- SA1015
|
||||||
|
# Trapping a signal that cannot be trapped.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA1016
|
||||||
|
- SA1016
|
||||||
|
# Channels used with 'os/signal.Notify' should be buffered.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA1017
|
||||||
|
- SA1017
|
||||||
|
# 'strings.Replace' called with 'n == 0', which does nothing.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA1018
|
||||||
|
- SA1018
|
||||||
|
# Using a deprecated function, variable, constant or field.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA1019
|
||||||
|
- SA1019
|
||||||
|
# Using an invalid host:port pair with a 'net.Listen'-related function.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA1020
|
||||||
|
- SA1020
|
||||||
|
# Using 'bytes.Equal' to compare two 'net.IP'.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA1021
|
||||||
|
- SA1021
|
||||||
|
# Modifying the buffer in an 'io.Writer' implementation.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA1023
|
||||||
|
- SA1023
|
||||||
|
# A string cutset contains duplicate characters.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA1024
|
||||||
|
- SA1024
|
||||||
|
# It is not possible to use '(*time.Timer).Reset''s return value correctly.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA1025
|
||||||
|
- SA1025
|
||||||
|
# Cannot marshal channels or functions.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA1026
|
||||||
|
- SA1026
|
||||||
|
# Atomic access to 64-bit variable must be 64-bit aligned.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA1027
|
||||||
|
- SA1027
|
||||||
|
# 'sort.Slice' can only be used on slices.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA1028
|
||||||
|
- SA1028
|
||||||
|
# Inappropriate key in call to 'context.WithValue'.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA1029
|
||||||
|
- SA1029
|
||||||
|
# Invalid argument in call to a 'strconv' function.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA1030
|
||||||
|
- SA1030
|
||||||
|
# Overlapping byte slices passed to an encoder.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA1031
|
||||||
|
- SA1031
|
||||||
|
# Wrong order of arguments to 'errors.Is'.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA1032
|
||||||
|
- SA1032
|
||||||
|
# 'sync.WaitGroup.Add' called inside the goroutine, leading to a race condition.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA2000
|
||||||
|
- SA2000
|
||||||
|
# Empty critical section, did you mean to defer the unlock?.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA2001
|
||||||
|
- SA2001
|
||||||
|
# Called 'testing.T.FailNow' or 'SkipNow' in a goroutine, which isn't allowed.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA2002
|
||||||
|
- SA2002
|
||||||
|
# Deferred 'Lock' right after locking, likely meant to defer 'Unlock' instead.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA2003
|
||||||
|
- SA2003
|
||||||
|
# 'TestMain' doesn't call 'os.Exit', hiding test failures.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA3000
|
||||||
|
- SA3000
|
||||||
|
# Assigning to 'b.N' in benchmarks distorts the results.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA3001
|
||||||
|
- SA3001
|
||||||
|
# Binary operator has identical expressions on both sides.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA4000
|
||||||
|
- SA4000
|
||||||
|
# '&*x' gets simplified to 'x', it does not copy 'x'.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA4001
|
||||||
|
- SA4001
|
||||||
|
# Comparing unsigned values against negative values is pointless.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA4003
|
||||||
|
- SA4003
|
||||||
|
# The loop exits unconditionally after one iteration.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA4004
|
||||||
|
- SA4004
|
||||||
|
# Field assignment that will never be observed. Did you mean to use a pointer receiver?.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA4005
|
||||||
|
- SA4005
|
||||||
|
# A value assigned to a variable is never read before being overwritten. Forgotten error check or dead code?.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA4006
|
||||||
|
- SA4006
|
||||||
|
# The variable in the loop condition never changes, are you incrementing the wrong variable?.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA4008
|
||||||
|
- SA4008
|
||||||
|
# A function argument is overwritten before its first use.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA4009
|
||||||
|
- SA4009
|
||||||
|
# The result of 'append' will never be observed anywhere.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA4010
|
||||||
|
- SA4010
|
||||||
|
# Break statement with no effect. Did you mean to break out of an outer loop?.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA4011
|
||||||
|
- SA4011
|
||||||
|
# Comparing a value against NaN even though no value is equal to NaN.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA4012
|
||||||
|
- SA4012
|
||||||
|
# Negating a boolean twice ('!!b') is the same as writing 'b'. This is either redundant, or a typo.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA4013
|
||||||
|
- SA4013
|
||||||
|
# An if/else if chain has repeated conditions and no side-effects; if the condition didn't match the first time, it won't match the second time, either.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA4014
|
||||||
|
- SA4014
|
||||||
|
# Calling functions like 'math.Ceil' on floats converted from integers doesn't do anything useful.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA4015
|
||||||
|
- SA4015
|
||||||
|
# Certain bitwise operations, such as 'x ^ 0', do not do anything useful.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA4016
|
||||||
|
- SA4016
|
||||||
|
# Discarding the return values of a function without side effects, making the call pointless.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA4017
|
||||||
|
- SA4017
|
||||||
|
# Self-assignment of variables.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA4018
|
||||||
|
- SA4018
|
||||||
|
# Multiple, identical build constraints in the same file.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA4019
|
||||||
|
- SA4019
|
||||||
|
# Unreachable case clause in a type switch.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA4020
|
||||||
|
- SA4020
|
||||||
|
# "x = append(y)" is equivalent to "x = y".
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA4021
|
||||||
|
- SA4021
|
||||||
|
# Comparing the address of a variable against nil.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA4022
|
||||||
|
- SA4022
|
||||||
|
# Impossible comparison of interface value with untyped nil.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA4023
|
||||||
|
- SA4023
|
||||||
|
# Checking for impossible return value from a builtin function.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA4024
|
||||||
|
- SA4024
|
||||||
|
# Integer division of literals that results in zero.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA4025
|
||||||
|
- SA4025
|
||||||
|
# Go constants cannot express negative zero.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA4026
|
||||||
|
- SA4026
|
||||||
|
# '(*net/url.URL).Query' returns a copy, modifying it doesn't change the URL.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA4027
|
||||||
|
- SA4027
|
||||||
|
# 'x % 1' is always zero.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA4028
|
||||||
|
- SA4028
|
||||||
|
# Ineffective attempt at sorting slice.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA4029
|
||||||
|
- SA4029
|
||||||
|
# Ineffective attempt at generating random number.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA4030
|
||||||
|
- SA4030
|
||||||
|
# Checking never-nil value against nil.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA4031
|
||||||
|
- SA4031
|
||||||
|
# Comparing 'runtime.GOOS' or 'runtime.GOARCH' against impossible value.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA4032
|
||||||
|
- SA4032
|
||||||
|
# Assignment to nil map.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA5000
|
||||||
|
- SA5000
|
||||||
|
# Deferring 'Close' before checking for a possible error.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA5001
|
||||||
|
- SA5001
|
||||||
|
# The empty for loop ("for {}") spins and can block the scheduler.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA5002
|
||||||
|
- SA5002
|
||||||
|
# Defers in infinite loops will never execute.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA5003
|
||||||
|
- SA5003
|
||||||
|
# "for { select { ..." with an empty default branch spins.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA5004
|
||||||
|
- SA5004
|
||||||
|
# The finalizer references the finalized object, preventing garbage collection.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA5005
|
||||||
|
- SA5005
|
||||||
|
# Infinite recursive call.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA5007
|
||||||
|
- SA5007
|
||||||
|
# Invalid struct tag.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA5008
|
||||||
|
- SA5008
|
||||||
|
# Invalid Printf call.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA5009
|
||||||
|
- SA5009
|
||||||
|
# Impossible type assertion.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA5010
|
||||||
|
- SA5010
|
||||||
|
# Possible nil pointer dereference.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA5011
|
||||||
|
- SA5011
|
||||||
|
# Passing odd-sized slice to function expecting even size.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA5012
|
||||||
|
- SA5012
|
||||||
|
# Using 'regexp.Match' or related in a loop, should use 'regexp.Compile'.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA6000
|
||||||
|
- SA6000
|
||||||
|
# Missing an optimization opportunity when indexing maps by byte slices.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA6001
|
||||||
|
- SA6001
|
||||||
|
# Storing non-pointer values in 'sync.Pool' allocates memory.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA6002
|
||||||
|
- SA6002
|
||||||
|
# Converting a string to a slice of runes before ranging over it.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA6003
|
||||||
|
- SA6003
|
||||||
|
# Inefficient string comparison with 'strings.ToLower' or 'strings.ToUpper'.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA6005
|
||||||
|
- SA6005
|
||||||
|
# Using io.WriteString to write '[]byte'.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA6006
|
||||||
|
- SA6006
|
||||||
|
# Defers in range loops may not run when you expect them to.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA9001
|
||||||
|
- SA9001
|
||||||
|
# Using a non-octal 'os.FileMode' that looks like it was meant to be in octal.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA9002
|
||||||
|
- SA9002
|
||||||
|
# Empty body in an if or else branch.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA9003
|
||||||
|
- SA9003
|
||||||
|
# Only the first constant has an explicit type.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA9004
|
||||||
|
- SA9004
|
||||||
|
# Trying to marshal a struct with no public fields nor custom marshaling.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA9005
|
||||||
|
- SA9005
|
||||||
|
# Dubious bit shifting of a fixed size integer value.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA9006
|
||||||
|
- SA9006
|
||||||
|
# Deleting a directory that shouldn't be deleted.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA9007
|
||||||
|
- SA9007
|
||||||
|
# 'else' branch of a type assertion is probably not reading the right value.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA9008
|
||||||
|
- SA9008
|
||||||
|
# Ineffectual Go compiler directive.
|
||||||
|
# https://staticcheck.dev/docs/checks/#SA9009
|
||||||
|
- SA9009
|
||||||
|
# Incorrect or missing package comment.
|
||||||
|
# https://staticcheck.dev/docs/checks/#ST1000
|
||||||
|
- ST1000
|
||||||
|
# Dot imports are discouraged.
|
||||||
|
# https://staticcheck.dev/docs/checks/#ST1001
|
||||||
|
- ST1001
|
||||||
|
# Poorly chosen identifier.
|
||||||
|
# https://staticcheck.dev/docs/checks/#ST1003
|
||||||
|
- ST1003
|
||||||
|
# Incorrectly formatted error string.
|
||||||
|
# https://staticcheck.dev/docs/checks/#ST1005
|
||||||
|
- ST1005
|
||||||
|
# Poorly chosen receiver name.
|
||||||
|
# https://staticcheck.dev/docs/checks/#ST1006
|
||||||
|
- ST1006
|
||||||
|
# A function's error value should be its last return value.
|
||||||
|
# https://staticcheck.dev/docs/checks/#ST1008
|
||||||
|
- ST1008
|
||||||
|
# Poorly chosen name for variable of type 'time.Duration'.
|
||||||
|
# https://staticcheck.dev/docs/checks/#ST1011
|
||||||
|
- ST1011
|
||||||
|
# Poorly chosen name for error variable.
|
||||||
|
# https://staticcheck.dev/docs/checks/#ST1012
|
||||||
|
- ST1012
|
||||||
|
# Should use constants for HTTP error codes, not magic numbers.
|
||||||
|
# https://staticcheck.dev/docs/checks/#ST1013
|
||||||
|
- ST1013
|
||||||
|
# A switch's default case should be the first or last case.
|
||||||
|
# https://staticcheck.dev/docs/checks/#ST1015
|
||||||
|
- ST1015
|
||||||
|
# Use consistent method receiver names.
|
||||||
|
# https://staticcheck.dev/docs/checks/#ST1016
|
||||||
|
- ST1016
|
||||||
|
# Don't use Yoda conditions.
|
||||||
|
# https://staticcheck.dev/docs/checks/#ST1017
|
||||||
|
- ST1017
|
||||||
|
# Avoid zero-width and control characters in string literals.
|
||||||
|
# https://staticcheck.dev/docs/checks/#ST1018
|
||||||
|
- ST1018
|
||||||
|
# Importing the same package multiple times.
|
||||||
|
# https://staticcheck.dev/docs/checks/#ST1019
|
||||||
|
- ST1019
|
||||||
|
# The documentation of an exported function should start with the function's name.
|
||||||
|
# https://staticcheck.dev/docs/checks/#ST1020
|
||||||
|
- ST1020
|
||||||
|
# The documentation of an exported type should start with type's name.
|
||||||
|
# https://staticcheck.dev/docs/checks/#ST1021
|
||||||
|
- ST1021
|
||||||
|
# The documentation of an exported variable or constant should start with variable's name.
|
||||||
|
# https://staticcheck.dev/docs/checks/#ST1022
|
||||||
|
- ST1022
|
||||||
|
# Redundant type in variable declaration.
|
||||||
|
# https://staticcheck.dev/docs/checks/#ST1023
|
||||||
|
- ST1023
|
||||||
|
# Use plain channel send or receive instead of single-case select.
|
||||||
|
# https://staticcheck.dev/docs/checks/#S1000
|
||||||
|
- S1000
|
||||||
|
# Replace for loop with call to copy.
|
||||||
|
# https://staticcheck.dev/docs/checks/#S1001
|
||||||
|
- S1001
|
||||||
|
# Omit comparison with boolean constant.
|
||||||
|
# https://staticcheck.dev/docs/checks/#S1002
|
||||||
|
- S1002
|
||||||
|
# Replace call to 'strings.Index' with 'strings.Contains'.
|
||||||
|
# https://staticcheck.dev/docs/checks/#S1003
|
||||||
|
- S1003
|
||||||
|
# Replace call to 'bytes.Compare' with 'bytes.Equal'.
|
||||||
|
# https://staticcheck.dev/docs/checks/#S1004
|
||||||
|
- S1004
|
||||||
|
# Drop unnecessary use of the blank identifier.
|
||||||
|
# https://staticcheck.dev/docs/checks/#S1005
|
||||||
|
- S1005
|
||||||
|
# Use "for { ... }" for infinite loops.
|
||||||
|
# https://staticcheck.dev/docs/checks/#S1006
|
||||||
|
- S1006
|
||||||
|
# Simplify regular expression by using raw string literal.
|
||||||
|
# https://staticcheck.dev/docs/checks/#S1007
|
||||||
|
- S1007
|
||||||
|
# Simplify returning boolean expression.
|
||||||
|
# https://staticcheck.dev/docs/checks/#S1008
|
||||||
|
- S1008
|
||||||
|
# Omit redundant nil check on slices, maps, and channels.
|
||||||
|
# https://staticcheck.dev/docs/checks/#S1009
|
||||||
|
- S1009
|
||||||
|
# Omit default slice index.
|
||||||
|
# https://staticcheck.dev/docs/checks/#S1010
|
||||||
|
- S1010
|
||||||
|
# Use a single 'append' to concatenate two slices.
|
||||||
|
# https://staticcheck.dev/docs/checks/#S1011
|
||||||
|
- S1011
|
||||||
|
# Replace 'time.Now().Sub(x)' with 'time.Since(x)'.
|
||||||
|
# https://staticcheck.dev/docs/checks/#S1012
|
||||||
|
- S1012
|
||||||
|
# Use a type conversion instead of manually copying struct fields.
|
||||||
|
# https://staticcheck.dev/docs/checks/#S1016
|
||||||
|
- S1016
|
||||||
|
# Replace manual trimming with 'strings.TrimPrefix'.
|
||||||
|
# https://staticcheck.dev/docs/checks/#S1017
|
||||||
|
- S1017
|
||||||
|
# Use "copy" for sliding elements.
|
||||||
|
# https://staticcheck.dev/docs/checks/#S1018
|
||||||
|
- S1018
|
||||||
|
# Simplify "make" call by omitting redundant arguments.
|
||||||
|
# https://staticcheck.dev/docs/checks/#S1019
|
||||||
|
- S1019
|
||||||
|
# Omit redundant nil check in type assertion.
|
||||||
|
# https://staticcheck.dev/docs/checks/#S1020
|
||||||
|
- S1020
|
||||||
|
# Merge variable declaration and assignment.
|
||||||
|
# https://staticcheck.dev/docs/checks/#S1021
|
||||||
|
- S1021
|
||||||
|
# Omit redundant control flow.
|
||||||
|
# https://staticcheck.dev/docs/checks/#S1023
|
||||||
|
- S1023
|
||||||
|
# Replace 'x.Sub(time.Now())' with 'time.Until(x)'.
|
||||||
|
# https://staticcheck.dev/docs/checks/#S1024
|
||||||
|
- S1024
|
||||||
|
# Don't use 'fmt.Sprintf("%s", x)' unnecessarily.
|
||||||
|
# https://staticcheck.dev/docs/checks/#S1025
|
||||||
|
- S1025
|
||||||
|
# Simplify error construction with 'fmt.Errorf'.
|
||||||
|
# https://staticcheck.dev/docs/checks/#S1028
|
||||||
|
- S1028
|
||||||
|
# Range over the string directly.
|
||||||
|
# https://staticcheck.dev/docs/checks/#S1029
|
||||||
|
- S1029
|
||||||
|
# Use 'bytes.Buffer.String' or 'bytes.Buffer.Bytes'.
|
||||||
|
# https://staticcheck.dev/docs/checks/#S1030
|
||||||
|
- S1030
|
||||||
|
# Omit redundant nil check around loop.
|
||||||
|
# https://staticcheck.dev/docs/checks/#S1031
|
||||||
|
- S1031
|
||||||
|
# Use 'sort.Ints(x)', 'sort.Float64s(x)', and 'sort.Strings(x)'.
|
||||||
|
# https://staticcheck.dev/docs/checks/#S1032
|
||||||
|
- S1032
|
||||||
|
# Unnecessary guard around call to "delete".
|
||||||
|
# https://staticcheck.dev/docs/checks/#S1033
|
||||||
|
- S1033
|
||||||
|
# Use result of type assertion to simplify cases.
|
||||||
|
# https://staticcheck.dev/docs/checks/#S1034
|
||||||
|
- S1034
|
||||||
|
# Redundant call to 'net/http.CanonicalHeaderKey' in method call on 'net/http.Header'.
|
||||||
|
# https://staticcheck.dev/docs/checks/#S1035
|
||||||
|
- S1035
|
||||||
|
# Unnecessary guard around map access.
|
||||||
|
# https://staticcheck.dev/docs/checks/#S1036
|
||||||
|
- S1036
|
||||||
|
# Elaborate way of sleeping.
|
||||||
|
# https://staticcheck.dev/docs/checks/#S1037
|
||||||
|
- S1037
|
||||||
|
# Unnecessarily complex way of printing formatted string.
|
||||||
|
# https://staticcheck.dev/docs/checks/#S1038
|
||||||
|
- S1038
|
||||||
|
# Unnecessary use of 'fmt.Sprint'.
|
||||||
|
# https://staticcheck.dev/docs/checks/#S1039
|
||||||
|
- S1039
|
||||||
|
# Type assertion to current type.
|
||||||
|
# https://staticcheck.dev/docs/checks/#S1040
|
||||||
|
- S1040
|
||||||
|
# Apply De Morgan's law.
|
||||||
|
# https://staticcheck.dev/docs/checks/#QF1001
|
||||||
|
- QF1001
|
||||||
|
# Convert untagged switch to tagged switch.
|
||||||
|
# https://staticcheck.dev/docs/checks/#QF1002
|
||||||
|
- QF1002
|
||||||
|
# Convert if/else-if chain to tagged switch.
|
||||||
|
# https://staticcheck.dev/docs/checks/#QF1003
|
||||||
|
- QF1003
|
||||||
|
# Use 'strings.ReplaceAll' instead of 'strings.Replace' with 'n == -1'.
|
||||||
|
# https://staticcheck.dev/docs/checks/#QF1004
|
||||||
|
- QF1004
|
||||||
|
# Expand call to 'math.Pow'.
|
||||||
|
# https://staticcheck.dev/docs/checks/#QF1005
|
||||||
|
- QF1005
|
||||||
|
# Lift 'if'+'break' into loop condition.
|
||||||
|
# https://staticcheck.dev/docs/checks/#QF1006
|
||||||
|
- QF1006
|
||||||
|
# Merge conditional assignment into variable declaration.
|
||||||
|
# https://staticcheck.dev/docs/checks/#QF1007
|
||||||
|
- QF1007
|
||||||
|
# Omit embedded fields from selector expression.
|
||||||
|
# https://staticcheck.dev/docs/checks/#QF1008
|
||||||
|
- QF1008
|
||||||
|
# Use 'time.Time.Equal' instead of '==' operator.
|
||||||
|
# https://staticcheck.dev/docs/checks/#QF1009
|
||||||
|
- QF1009
|
||||||
|
# Convert slice of bytes to string when printing it.
|
||||||
|
# https://staticcheck.dev/docs/checks/#QF1010
|
||||||
|
- QF1010
|
||||||
|
# Omit redundant type from variable declaration.
|
||||||
|
# https://staticcheck.dev/docs/checks/#QF1011
|
||||||
|
- QF1011
|
||||||
|
# Use 'fmt.Fprintf(x, ...)' instead of 'x.Write(fmt.Sprintf(...))'.
|
||||||
|
# https://staticcheck.dev/docs/checks/#QF1012
|
||||||
|
- QF1012
|
||||||
|
unconvert:
|
||||||
|
# Remove conversions that force intermediate rounding.
|
||||||
|
# Default: false
|
||||||
|
fast-math: true
|
||||||
|
# Be more conservative (experimental).
|
||||||
|
# Default: false
|
||||||
|
safe: true
|
||||||
|
usestdlibvars:
|
||||||
|
# Suggest the use of http.MethodXX.
|
||||||
|
# Default: true
|
||||||
|
http-method: false
|
||||||
|
# Suggest the use of http.StatusXX.
|
||||||
|
# Default: true
|
||||||
|
http-status-code: false
|
||||||
|
# Suggest the use of time.Month in time.Date.
|
||||||
|
# Default: false
|
||||||
|
time-date-month: true
|
||||||
|
# Suggest the use of time.Weekday.String().
|
||||||
|
# Default: true
|
||||||
|
time-weekday: true
|
||||||
|
# Suggest the use of time.Month.String().
|
||||||
|
# Default: false
|
||||||
|
time-month: true
|
||||||
|
# Suggest the use of time.Layout.
|
||||||
|
# Default: false
|
||||||
|
time-layout: true
|
||||||
|
# Suggest the use of crypto.Hash.String().
|
||||||
|
# Default: false
|
||||||
|
crypto-hash: true
|
||||||
|
# Suggest the use of rpc.DefaultXXPath.
|
||||||
|
# Default: false
|
||||||
|
default-rpc-path: true
|
||||||
|
# Suggest the use of sql.LevelXX.String().
|
||||||
|
# Default: false
|
||||||
|
sql-isolation-level: true
|
||||||
|
# Suggest the use of tls.SignatureScheme.String().
|
||||||
|
# Default: false
|
||||||
|
tls-signature-scheme: true
|
||||||
|
# Suggest the use of constant.Kind.String().
|
||||||
|
# Default: false
|
||||||
|
constant-kind: true
|
||||||
|
wsl_v5:
|
||||||
|
allow-first-in-block: true
|
||||||
|
allow-whole-block: true
|
||||||
|
branch-max-lines: 2
|
||||||
|
case-max-lines: 2
|
||||||
|
default: all
|
||||||
|
disable:
|
||||||
|
- if
|
||||||
|
- err
|
||||||
|
exclusions:
|
||||||
|
generated: lax
|
||||||
|
presets:
|
||||||
|
- comments
|
||||||
|
- common-false-positives
|
||||||
|
- legacy
|
||||||
|
- std-error-handling
|
||||||
|
paths:
|
||||||
|
- third_party$
|
||||||
|
- builtin$
|
||||||
|
- examples$
|
||||||
|
formatters:
|
||||||
|
enable:
|
||||||
|
- gofmt
|
||||||
|
- goimports
|
||||||
|
exclusions:
|
||||||
|
generated: lax
|
||||||
|
paths:
|
||||||
|
- third_party$
|
||||||
|
- builtin$
|
||||||
|
- examples$
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
|
// Package main содержит основную функцию для запуска логики бота.
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"regexp"
|
"regexp"
|
||||||
@@ -11,75 +13,55 @@ import (
|
|||||||
_ "embed"
|
_ "embed"
|
||||||
|
|
||||||
"buny-jabber-bot/internal/jabber"
|
"buny-jabber-bot/internal/jabber"
|
||||||
|
"buny-jabber-bot/internal/log"
|
||||||
|
|
||||||
"github.com/eleksir/go-xmpp"
|
"github.com/xmppo/go-xmpp"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"gopkg.in/tomb.v2"
|
"gopkg.in/tomb.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed version
|
//go:embed version
|
||||||
var version string
|
var version string //nolint: grouper
|
||||||
|
|
||||||
// main - фактичеcки, начало и основное тело программы.
|
// main - фактичеcки, начало и основное тело программы.
|
||||||
func main() {
|
func main() {
|
||||||
var err error
|
var (
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
var j = jabber.Jabber{ //nolint:exhaustruct
|
var (
|
||||||
SigChan: make(chan os.Signal, 1),
|
logfile = os.Stderr
|
||||||
GTomb: tomb.Tomb{},
|
j = jabber.Jabber{ //nolint:exhaustruct
|
||||||
RoomsConnected: make([]string, 1),
|
SigChan: make(chan os.Signal, 1),
|
||||||
}
|
GTomb: tomb.Tomb{},
|
||||||
|
RoomsConnected: make([]string, 1),
|
||||||
log.SetFormatter(&log.TextFormatter{ //nolint:exhaustruct
|
}
|
||||||
DisableQuote: true,
|
)
|
||||||
DisableLevelTruncation: false,
|
|
||||||
DisableColors: true,
|
|
||||||
FullTimestamp: true,
|
|
||||||
TimestampFormat: "2006-01-02 15:04:05",
|
|
||||||
})
|
|
||||||
|
|
||||||
if err := j.ReadConfig(); err != nil {
|
if err := j.ReadConfig(); err != nil {
|
||||||
log.Error(err)
|
log.Errorf("%s", err)
|
||||||
|
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
j.C.Version = version
|
j.C.Version = version
|
||||||
|
|
||||||
// no panic
|
|
||||||
switch j.C.Loglevel {
|
|
||||||
case "fatal":
|
|
||||||
log.SetLevel(log.FatalLevel)
|
|
||||||
case "error":
|
|
||||||
log.SetLevel(log.ErrorLevel)
|
|
||||||
case "warn":
|
|
||||||
log.SetLevel(log.WarnLevel)
|
|
||||||
case "info":
|
|
||||||
log.SetLevel(log.InfoLevel)
|
|
||||||
case "debug":
|
|
||||||
log.SetLevel(log.DebugLevel)
|
|
||||||
case "trace":
|
|
||||||
log.SetLevel(log.TraceLevel)
|
|
||||||
default:
|
|
||||||
log.SetLevel(log.InfoLevel)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Откроем лог и скормим его логгеру.
|
// Откроем лог и скормим его логгеру.
|
||||||
if j.C.Log != "" {
|
if j.C.Log != "" {
|
||||||
logfile, err := os.OpenFile(j.C.Log, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
|
logfile, err = os.OpenFile(j.C.Log, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Unable to open log file %s: %s", j.C.Log, err)
|
log.Errorf("Unable to open log file %s: %s", j.C.Log, err)
|
||||||
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.SetOutput(logfile)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Init(j.C.Loglevel, logfile)
|
||||||
|
|
||||||
j.C.ExeName, err = os.Executable()
|
j.C.ExeName, err = os.Executable()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panicf("Unable to find my executable: %s", err)
|
log.Fatalf("Unable to find my executable: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle pricky windows case. But honestly i do not expect it runs on windows.
|
// handle pricky windows case. But honestly i do not expect it runs on windows.
|
||||||
@@ -98,26 +80,25 @@ func main() {
|
|||||||
|
|
||||||
verboseClient := false
|
verboseClient := false
|
||||||
|
|
||||||
if myLogLevel == log.TraceLevel {
|
if log.LogLevel == slog.LevelDebug {
|
||||||
verboseClient = true
|
if j.C.Debug.Lib {
|
||||||
|
verboseClient = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Самое время вгрузить белые и чёрные списки, если что - получим ошибку в лог.
|
// Самое время вгрузить белые и чёрные списки, если что - получим ошибку в лог.
|
||||||
if err := j.ReadWhitelist(); err != nil {
|
if err := j.ReadWhitelist(); err != nil {
|
||||||
log.Error(err)
|
log.Error(err.Error())
|
||||||
|
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := j.ReadBlacklist(); err != nil {
|
if err := j.ReadBlacklist(); err != nil {
|
||||||
log.Error(err)
|
log.Error(err.Error())
|
||||||
|
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// github.com/mattn/go-xmpp пишет в stdio, нам этого не надо, ловим выхлоп его в logrus с уровнем trace.
|
|
||||||
xmpp.DebugWriter = log.WithFields(log.Fields{"logger": "stdlib"}).WriterLevel(log.TraceLevel)
|
|
||||||
|
|
||||||
// Хэндлер сигналов.
|
// Хэндлер сигналов.
|
||||||
j.GTomb.Go(func() error { return j.SigHandler() }) //nolint: gocritic
|
j.GTomb.Go(func() error { return j.SigHandler() }) //nolint: gocritic
|
||||||
|
|
||||||
@@ -140,6 +121,12 @@ func main() {
|
|||||||
Status: "xa",
|
Status: "xa",
|
||||||
StatusMessage: jabber.RandomPhrase(j.C.Jabber.StartupStatus),
|
StatusMessage: jabber.RandomPhrase(j.C.Jabber.StartupStatus),
|
||||||
DialTimeout: time.Duration(j.C.Jabber.ConnectionTimeout) * time.Second,
|
DialTimeout: time.Duration(j.C.Jabber.ConnectionTimeout) * time.Second,
|
||||||
|
DebugWriter: log.Writer,
|
||||||
|
PeriodicServerPings: false,
|
||||||
|
ReportSoftwareVersion: true,
|
||||||
|
SoftwareName: "Buny Jabber Bot",
|
||||||
|
SoftwareVersion: version,
|
||||||
|
ReportSoftwareOS: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Враппер основной программы. Фактически на каждой серьёзной ошибке, например, сетевой, mainLoop и некоторые
|
// Враппер основной программы. Фактически на каждой серьёзной ошибке, например, сетевой, mainLoop и некоторые
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
|
|
||||||
"user_agent": [
|
"user_agent": [
|
||||||
{
|
{
|
||||||
"software": "BadUserAgent",
|
"name": "BadUserAgent",
|
||||||
"version": "0.666.0",
|
"version": "0.666.0",
|
||||||
"os": "DeathStarOS"
|
"os": "DeathStarOS"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -196,5 +196,16 @@
|
|||||||
"loglevel": "debug",
|
"loglevel": "debug",
|
||||||
|
|
||||||
# Если не задан, логи идут в STDOUT
|
# Если не задан, логи идут в STDOUT
|
||||||
"log": "/var/log/buny-jabber-bot/error.log"
|
"log": "/var/log/buny-jabber-bot/error.log",
|
||||||
|
|
||||||
|
# Некоторый дебаг создаёт лишний шум. Самые "шумные" опции вынесены сюда.
|
||||||
|
"debug": {
|
||||||
|
# Это дебаг самой либы go-xmpp. Это бывает полезно, если надо похэндлить какой-то формат xmpp-сообщений, т.к.
|
||||||
|
# логгирует то, что пишется "в провод"
|
||||||
|
"lib": false,
|
||||||
|
# Дебаг периодических пингов. Мало полезная вещь, разве что для выявления сетевых аномалий.
|
||||||
|
"ping": false,
|
||||||
|
# Дебаг работы presence db.
|
||||||
|
"presence_db": false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
13
go.mod
13
go.mod
@@ -4,18 +4,11 @@ go 1.24.3
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/davecgh/go-spew v1.1.1
|
github.com/davecgh/go-spew v1.1.1
|
||||||
github.com/eleksir/go-xmpp v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/hjson/hjson-go v3.3.0+incompatible
|
github.com/hjson/hjson-go v3.3.0+incompatible
|
||||||
github.com/jbrukh/bayesian v0.0.0-20231117143245-13ae6f916c7a
|
github.com/jbrukh/bayesian v0.0.0-20231117143245-13ae6f916c7a
|
||||||
github.com/sirupsen/logrus v1.9.3
|
github.com/xmppo/go-xmpp v0.3.1
|
||||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6
|
|
||||||
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637
|
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require golang.org/x/net v0.48.0 // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
|
||||||
golang.org/x/crypto v0.38.0 // indirect
|
|
||||||
golang.org/x/net v0.40.0 // indirect
|
|
||||||
golang.org/x/sys v0.33.0 // indirect
|
|
||||||
golang.org/x/text v0.25.0 // indirect
|
|
||||||
)
|
|
||||||
|
|||||||
32
go.sum
32
go.sum
@@ -1,34 +1,18 @@
|
|||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/eleksir/go-xmpp v1.6.0 h1:bkGr0YVczJWpkyk/IHhZb2ewv+NeHLvdNfds0TFgqao=
|
|
||||||
github.com/eleksir/go-xmpp v1.6.0/go.mod h1:Ii6Wemk+yuGQOF+Glt0syjvtcPvdaBLstuNp/+rRJrg=
|
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/hjson/hjson-go v3.3.0+incompatible h1:Rqr+Ya+0aCJMjaE4s8E9YKvuJLuLVpEvz4ONum52vnI=
|
github.com/hjson/hjson-go v3.3.0+incompatible h1:Rqr+Ya+0aCJMjaE4s8E9YKvuJLuLVpEvz4ONum52vnI=
|
||||||
github.com/hjson/hjson-go v3.3.0+incompatible/go.mod h1:qsetwF8NlsTsOTwZTApNlTCerV+b2GjYRRcIk4JMFio=
|
github.com/hjson/hjson-go v3.3.0+incompatible/go.mod h1:qsetwF8NlsTsOTwZTApNlTCerV+b2GjYRRcIk4JMFio=
|
||||||
github.com/jbrukh/bayesian v0.0.0-20231117143245-13ae6f916c7a h1:zSc8tkEGo49/E6cQ9o00si0L9Q/Lu1Hsg8JWKCvzEDU=
|
github.com/jbrukh/bayesian v0.0.0-20231117143245-13ae6f916c7a h1:zSc8tkEGo49/E6cQ9o00si0L9Q/Lu1Hsg8JWKCvzEDU=
|
||||||
github.com/jbrukh/bayesian v0.0.0-20231117143245-13ae6f916c7a/go.mod h1:SELxwZQq/mPnfPCR2mchLmT4TQaPJvYtLcCtDWSM7vM=
|
github.com/jbrukh/bayesian v0.0.0-20231117143245-13ae6f916c7a/go.mod h1:SELxwZQq/mPnfPCR2mchLmT4TQaPJvYtLcCtDWSM7vM=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/xmppo/go-xmpp v0.2.19-0.20251123101827-6986f11d85b8 h1:Q6MDZxJVNPlwH/Pm3uQhXhdA2eSUP8YMmhSkCUHbfLU=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/xmppo/go-xmpp v0.2.19-0.20251123101827-6986f11d85b8/go.mod h1:RyX2+ufcANlJ/ItVhrvfzy4vrvr4537qpPXRsOB+Nz4=
|
||||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
github.com/xmppo/go-xmpp v0.3.1 h1:GZUCGZozZdfvv25FVvlgwKoNdRjmfJCUcVG8349/E4A=
|
||||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
github.com/xmppo/go-xmpp v0.3.1/go.mod h1:EBLbzPt4Y9OJBEF58Lhc4IrTnO226aIkbfosQo6KWeA=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
||||||
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
|
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
||||||
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
|
||||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI=
|
|
||||||
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
|
|
||||||
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
|
||||||
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
|
||||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
|
||||||
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
|
|
||||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 h1:yiW+nvdHb9LVqSHQBXfZCieqV4fzYhNBql77zY0ykqs=
|
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 h1:yiW+nvdHb9LVqSHQBXfZCieqV4fzYhNBql77zY0ykqs=
|
||||||
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637/go.mod h1:BHsqpu/nsuzkT5BpiH1EMZPLyqSMM8JbIavyFACoFNk=
|
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637/go.mod h1:BHsqpu/nsuzkT5BpiH1EMZPLyqSMM8JbIavyFACoFNk=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
|
|||||||
@@ -1,22 +1,28 @@
|
|||||||
|
// Package jabber contains all buny jabber bot guts.
|
||||||
package jabber
|
package jabber
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"buny-jabber-bot/internal/log"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/jbrukh/bayesian"
|
"github.com/jbrukh/bayesian"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
goodDictionaryFile string = "./data/good_dictionary.txt"
|
// goodDictionaryFile путь до файла с заведомо хорошими фразами.
|
||||||
badDictionaryFile string = "./data/bad_dictionary.txt"
|
goodDictionaryFile string = "./data/good_dictionary.txt" //nolint: unused
|
||||||
dataFile string = "./data/data.bin"
|
// badDictionaryFile путь до файла с заведемо плохими фразами.
|
||||||
Bad bayesian.Class = "Bad"
|
badDictionaryFile string = "./data/bad_dictionary.txt" //nolint: unused
|
||||||
Good bayesian.Class = "Good"
|
// dataFile путь до файла с данными тренировки фильтра.
|
||||||
|
dataFile string = "./data/data.bin" //nolint: unused
|
||||||
|
// Bad bayessian class with bad items.
|
||||||
|
Bad bayesian.Class = "Bad"
|
||||||
|
// Good bayessian class with good items.
|
||||||
|
Good bayesian.Class = "Good"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Выдаёт "очки похожести" для данной фразы.
|
// Выдаёт "очки похожести" для данной фразы.
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
package jabber
|
package jabber
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"buny-jabber-bot/internal/log"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/davecgh/go-spew/spew"
|
||||||
"github.com/hjson/hjson-go"
|
"github.com/hjson/hjson-go"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ReadWhitelist читает и валидирует белые списки пользователей.
|
// ReadWhitelist читает и валидирует белые списки пользователей.
|
||||||
@@ -27,7 +29,7 @@ func (j *Jabber) ReadWhitelist() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
whitelistJSONPath := fmt.Sprintf("%s/data/whitelist.json", filepath.Dir(executablePath))
|
whitelistJSONPath := filepath.Dir(executablePath) + "/data/whitelist.json"
|
||||||
|
|
||||||
locations := []string{
|
locations := []string{
|
||||||
"~/.buny-jabber-bot-whitelist.json",
|
"~/.buny-jabber-bot-whitelist.json",
|
||||||
@@ -39,12 +41,12 @@ func (j *Jabber) ReadWhitelist() error {
|
|||||||
for _, location := range locations {
|
for _, location := range locations {
|
||||||
fileInfo, err := os.Stat(location)
|
fileInfo, err := os.Stat(location)
|
||||||
|
|
||||||
// Предполагаем, что файла либо нет, либо мы не можем его прочитать, второе надо бы логгировать, но пока забьём
|
// Предполагаем, что файла либо нет, либо мы не можем его прочитать, второе надо бы логгировать, но пока забьём.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Файл белого списка длинноват для белого списка, попробуем следующего кандидата
|
// Файл белого списка длинноват для белого списка, попробуем следующего кандидата.
|
||||||
if fileInfo.Size() > 2097152 {
|
if fileInfo.Size() > 2097152 {
|
||||||
log.Warnf("Whitelist file %s is too long for whitelist, skipping", location)
|
log.Warnf("Whitelist file %s is too long for whitelist, skipping", location)
|
||||||
|
|
||||||
@@ -53,7 +55,7 @@ func (j *Jabber) ReadWhitelist() error {
|
|||||||
|
|
||||||
buf, err := os.ReadFile(location)
|
buf, err := os.ReadFile(location)
|
||||||
|
|
||||||
// Не удалось прочитать, попробуем следующего кандидата
|
// Не удалось прочитать, попробуем следующего кандидата.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("Skip reading whitelist file %s: %s", location, err)
|
log.Warnf("Skip reading whitelist file %s: %s", location, err)
|
||||||
|
|
||||||
@@ -65,12 +67,12 @@ func (j *Jabber) ReadWhitelist() error {
|
|||||||
// сериализуем в json, а потом json превращаем в структурку. Не очень эффективно, но он и нечасто требуется.
|
// сериализуем в json, а потом json превращаем в структурку. Не очень эффективно, но он и нечасто требуется.
|
||||||
var (
|
var (
|
||||||
sampleWhitelist MyWhiteList
|
sampleWhitelist MyWhiteList
|
||||||
tmp map[string]interface{}
|
tmp map[string]any
|
||||||
)
|
)
|
||||||
|
|
||||||
err = hjson.Unmarshal(buf, &tmp)
|
err = hjson.Unmarshal(buf, &tmp)
|
||||||
|
|
||||||
// Не удалось распарсить - попробуем следующего кандидата
|
// Не удалось распарсить - попробуем следующего кандидата.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("Skip parsing whitelist file %s: %s", location, err)
|
log.Warnf("Skip parsing whitelist file %s: %s", location, err)
|
||||||
|
|
||||||
@@ -79,7 +81,7 @@ func (j *Jabber) ReadWhitelist() error {
|
|||||||
|
|
||||||
tmpJSON, err := json.Marshal(tmp)
|
tmpJSON, err := json.Marshal(tmp)
|
||||||
|
|
||||||
// Не удалось преобразовать map-ку в json
|
// Не удалось преобразовать map-ку в json.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("Skip parsing whitelist file %s: %s", location, err)
|
log.Warnf("Skip parsing whitelist file %s: %s", location, err)
|
||||||
|
|
||||||
@@ -123,24 +125,24 @@ func (j *Jabber) ReadBlacklist() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
whitelistJSONPath := fmt.Sprintf("%s/data/blacklist.json", filepath.Dir(executablePath))
|
blacklistJSONPath := filepath.Dir(executablePath) + "/data/blacklist.json"
|
||||||
|
|
||||||
locations := []string{
|
locations := []string{
|
||||||
"~/.bunyPresense-jabber-bot-blacklist.json",
|
"~/.buny-jabber-bot-blacklist.json",
|
||||||
"~/bunyPresense-jabber-bot-blacklist.json",
|
"~/buny-jabber-bot-blacklist.json",
|
||||||
"/etc/bunyPresense-jabber-bot-blacklist.json",
|
"/etc/buny-jabber-bot-blacklist.json",
|
||||||
whitelistJSONPath,
|
blacklistJSONPath,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, location := range locations {
|
for _, location := range locations {
|
||||||
fileInfo, err := os.Stat(location)
|
fileInfo, err := os.Stat(location)
|
||||||
|
|
||||||
// Предполагаем, что файла либо нет, либо мы не можем его прочитать, второе надо бы логгировать, но пока забьём
|
// Предполагаем, что файла либо нет, либо мы не можем его прочитать, второе надо бы логгировать, но пока забьём.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Файл чёрного списка длинноват для чёрного списка, попробуем следующего кандидата
|
// Файл чёрного списка длинноват для чёрного списка, попробуем следующего кандидата.
|
||||||
if fileInfo.Size() > 16777216 {
|
if fileInfo.Size() > 16777216 {
|
||||||
log.Warnf("Blacklist file %s is too long for blacklist, skipping", location)
|
log.Warnf("Blacklist file %s is too long for blacklist, skipping", location)
|
||||||
|
|
||||||
@@ -149,7 +151,7 @@ func (j *Jabber) ReadBlacklist() error {
|
|||||||
|
|
||||||
buf, err := os.ReadFile(location)
|
buf, err := os.ReadFile(location)
|
||||||
|
|
||||||
// Не удалось прочитать, попробуем следующего кандидата
|
// Не удалось прочитать, попробуем следующего кандидата.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("Skip reading blacklist file %s: %s", location, err)
|
log.Warnf("Skip reading blacklist file %s: %s", location, err)
|
||||||
|
|
||||||
@@ -161,12 +163,12 @@ func (j *Jabber) ReadBlacklist() error {
|
|||||||
// сериализуем в json, а потом json превращаем в структурку. Не очень эффективно, но он и нечасто требуется.
|
// сериализуем в json, а потом json превращаем в структурку. Не очень эффективно, но он и нечасто требуется.
|
||||||
var (
|
var (
|
||||||
sampleBlacklist MyBlackList
|
sampleBlacklist MyBlackList
|
||||||
tmp map[string]interface{}
|
tmp map[string]any
|
||||||
)
|
)
|
||||||
|
|
||||||
err = hjson.Unmarshal(buf, &tmp)
|
err = hjson.Unmarshal(buf, &tmp)
|
||||||
|
|
||||||
// Не удалось распарсить - попробуем следующего кандидата
|
// Не удалось распарсить - попробуем следующего кандидата.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("Skip parsing blacklist file %s: %s", location, err)
|
log.Warnf("Skip parsing blacklist file %s: %s", location, err)
|
||||||
|
|
||||||
@@ -175,7 +177,7 @@ func (j *Jabber) ReadBlacklist() error {
|
|||||||
|
|
||||||
tmpJSON, err := json.Marshal(tmp)
|
tmpJSON, err := json.Marshal(tmp)
|
||||||
|
|
||||||
// Не удалось преобразовать map-ку в json
|
// Не удалось преобразовать map-ку в json.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("Skip parsing blacklist file %s: %s", location, err)
|
log.Warnf("Skip parsing blacklist file %s: %s", location, err)
|
||||||
|
|
||||||
@@ -188,7 +190,19 @@ func (j *Jabber) ReadBlacklist() error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
j.BlackList = sampleBlacklist
|
for _, entry := range sampleBlacklist.Blacklist {
|
||||||
|
for str := range strings.SplitSeq(spew.Sdump(entry), "\n") {
|
||||||
|
log.Debug(str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Вообще говоря, нас уверяют, что простого "=" хватает, но на практике оказывается, что вложенные стуктуры
|
||||||
|
// могут и не скопироваться.
|
||||||
|
tmpjson, _ := json.Marshal(sampleBlacklist) //nolint: errchkjson // Да блять, сколько можно
|
||||||
|
// доёбываться, эти данные 100500 раз перекодировались, там ошибка возможна только если есть ошибка в
|
||||||
|
// encoding/json, но в этом случае мы в любом случае ничего поделать не можем, вот всяком случае в рантайме.
|
||||||
|
_ = json.Unmarshal(tmpjson, &j.BlackList)
|
||||||
|
|
||||||
blacklistLoaded = true
|
blacklistLoaded = true
|
||||||
|
|
||||||
log.Infof("Using %s as blacklist file", location)
|
log.Infof("Using %s as blacklist file", location)
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
package jabber
|
package jabber
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"buny-jabber-bot/internal/log"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
"github.com/eleksir/go-xmpp"
|
"github.com/xmppo/go-xmpp"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// BunyPresense производит проверку по бело-чёрным спискам. Если presence пришёл от злодея (из чёрного списка), то
|
// BunyPresense производит проверку по бело-чёрным спискам. Если presence пришёл от злодея (из чёрного списка), то
|
||||||
@@ -17,9 +18,9 @@ func (j *Jabber) BunyPresense(v xmpp.Presence) error { //nolint:gocognit,gocyclo
|
|||||||
var err error
|
var err error
|
||||||
|
|
||||||
// Если у presence-а есть JID и presence из одной из комнат, в которой мы есть и если его домен в чёрном
|
// Если у presence-а есть JID и presence из одной из комнат, в которой мы есть и если его домен в чёрном
|
||||||
// списке, заносим пидора в список outcast-ов
|
// списке, заносим пидора в список outcast-ов.
|
||||||
if v.JID != "" {
|
if v.JID != "" {
|
||||||
// На всякий случай: себя никогда не баним, явным образом
|
// На всякий случай: себя никогда не баним, явным образом.
|
||||||
if v.JID == j.Talk.JID() {
|
if v.JID == j.Talk.JID() {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -27,30 +28,30 @@ func (j *Jabber) BunyPresense(v xmpp.Presence) error { //nolint:gocognit,gocyclo
|
|||||||
room := strings.SplitN(v.From, "/", 2)[0]
|
room := strings.SplitN(v.From, "/", 2)[0]
|
||||||
|
|
||||||
if room == "" {
|
if room == "" {
|
||||||
log.Infof("We got empty room field in presence event, which kinda strange: %s", spew.Sdump(v))
|
msg := "We got empty room field in presence event, which kinda strange: " + spew.Sdump(v)
|
||||||
|
|
||||||
|
for str := range strings.SplitSeq(msg, "\n") {
|
||||||
|
log.Info(str)
|
||||||
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
goodJid := strings.SplitN(v.JID, "/", 2)[0]
|
goodJid := strings.SplitN(v.JID, "/", 2)[0]
|
||||||
|
|
||||||
// Обрабатываем правила белого списка
|
// Обрабатываем правила белого списка.
|
||||||
for _, good := range j.WhiteList.Whitelist {
|
for _, good := range j.WhiteList.Whitelist {
|
||||||
// Глобальный белый список
|
// Глобальный белый список.
|
||||||
if good.RoomName == "" {
|
if good.RoomName == "" {
|
||||||
for _, person := range good.Jid {
|
if slices.Contains(good.Jid, goodJid) {
|
||||||
if person == goodJid {
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Белый список для конкретных каналов
|
// Белый список для конкретных каналов.
|
||||||
if good.RoomName == room {
|
if good.RoomName == room {
|
||||||
for _, person := range good.Jid {
|
if slices.Contains(good.Jid, goodJid) {
|
||||||
if person == goodJid {
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -74,16 +75,16 @@ func (j *Jabber) BunyPresense(v xmpp.Presence) error { //nolint:gocognit,gocyclo
|
|||||||
evilNick := ""
|
evilNick := ""
|
||||||
evilNicks := strings.SplitN(v.From, "/", 2)
|
evilNicks := strings.SplitN(v.From, "/", 2)
|
||||||
|
|
||||||
// Давайте-ка похэндлим нонсенс - когда у нас в строке нету разделителя
|
// Давайте-ка похэндлим нонсенс - когда у нас в строке нету разделителя.
|
||||||
if len(evilNicks) > 1 {
|
if len(evilNicks) > 1 {
|
||||||
evilNick = evilNicks[1]
|
evilNick = evilNicks[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Обрабатываем правила чёрного списка
|
// Обрабатываем правила чёрного списка.
|
||||||
for _, cRoom := range j.RoomsConnected {
|
for _, cRoom := range j.RoomsConnected {
|
||||||
if cRoom == room {
|
if cRoom == room {
|
||||||
for _, bEntry := range j.BlackList.Blacklist {
|
for _, bEntry := range j.BlackList.Blacklist {
|
||||||
// Обработаем правила глобального чёрного списка
|
// Обработаем правила глобального чёрного списка.
|
||||||
if bEntry.RoomName == "" {
|
if bEntry.RoomName == "" {
|
||||||
for _, jidRegexp := range bEntry.JidRe {
|
for _, jidRegexp := range bEntry.JidRe {
|
||||||
if jidRegexp == "" {
|
if jidRegexp == "" {
|
||||||
@@ -147,7 +148,7 @@ func (j *Jabber) BunyPresense(v xmpp.Presence) error { //nolint:gocognit,gocyclo
|
|||||||
nickRegexp,
|
nickRegexp,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Баним именно jid
|
// Баним именно jid.
|
||||||
id, err := j.Squash(room, evilJid, bEntry.ReasonEnable, v.Type)
|
id, err := j.Squash(room, evilJid, bEntry.ReasonEnable, v.Type)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -167,7 +168,7 @@ func (j *Jabber) BunyPresense(v xmpp.Presence) error { //nolint:gocognit,gocyclo
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Обработаем правила конкретного канала, комнаты конференции
|
// Обработаем правила конкретного канала, комнаты конференции.
|
||||||
if bEntry.RoomName == room {
|
if bEntry.RoomName == room {
|
||||||
for _, jidRegexp := range bEntry.JidRe {
|
for _, jidRegexp := range bEntry.JidRe {
|
||||||
if jidRegexp == "" {
|
if jidRegexp == "" {
|
||||||
@@ -232,7 +233,7 @@ func (j *Jabber) BunyPresense(v xmpp.Presence) error { //nolint:gocognit,gocyclo
|
|||||||
nickRegexp,
|
nickRegexp,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Баним именно jid
|
// Баним именно jid.
|
||||||
id, err := j.Squash(room, evilJid, bEntry.ReasonEnable, v.Type)
|
id, err := j.Squash(room, evilJid, bEntry.ReasonEnable, v.Type)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -265,8 +266,7 @@ func (j *Jabber) BunyPresense(v xmpp.Presence) error { //nolint:gocognit,gocyclo
|
|||||||
func (j *Jabber) BunyChat(v xmpp.Chat) error {
|
func (j *Jabber) BunyChat(v xmpp.Chat) error {
|
||||||
var (
|
var (
|
||||||
room = (strings.SplitN(v.Remote, "/", 2))[0]
|
room = (strings.SplitN(v.Remote, "/", 2))[0]
|
||||||
// nick = (strings.SplitN(v.Remote, "/", 2))[1]
|
err error
|
||||||
err error
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Действовать мы можем только в рамках тех комнат, где явно присуствуем.
|
// Действовать мы можем только в рамках тех комнат, где явно присуствуем.
|
||||||
@@ -274,7 +274,7 @@ func (j *Jabber) BunyChat(v xmpp.Chat) error {
|
|||||||
if cRoom == room {
|
if cRoom == room {
|
||||||
// Перебирём правила чёрных списков.
|
// Перебирём правила чёрных списков.
|
||||||
for _, bEntry := range j.BlackList.Blacklist {
|
for _, bEntry := range j.BlackList.Blacklist {
|
||||||
// Обработаем правила глобального чёрного списка
|
// Обработаем правила глобального чёрного списка.
|
||||||
if bEntry.RoomName == "" {
|
if bEntry.RoomName == "" {
|
||||||
for _, phraseRegexp := range bEntry.PhraseRe {
|
for _, phraseRegexp := range bEntry.PhraseRe {
|
||||||
if phraseRegexp == "" {
|
if phraseRegexp == "" {
|
||||||
@@ -317,7 +317,7 @@ func (j *Jabber) BunyChat(v xmpp.Chat) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Обработаем правила чёрного списка для конкретной комнаты
|
// Обработаем правила чёрного списка для конкретной комнаты.
|
||||||
if bEntry.RoomName == room {
|
if bEntry.RoomName == room {
|
||||||
for _, phraseRegexp := range bEntry.PhraseRe {
|
for _, phraseRegexp := range bEntry.PhraseRe {
|
||||||
if phraseRegexp == "" {
|
if phraseRegexp == "" {
|
||||||
@@ -364,10 +364,10 @@ func (j *Jabber) BunyChat(v xmpp.Chat) error {
|
|||||||
// Если включено, проверяем фразу на КАПС.
|
// Если включено, проверяем фразу на КАПС.
|
||||||
for _, channel := range j.C.Jabber.Channels {
|
for _, channel := range j.C.Jabber.Channels {
|
||||||
if channel.Name == room && channel.AllCaps.Enabled {
|
if channel.Name == room && channel.AllCaps.Enabled {
|
||||||
// Нормализуем строку и вырежем из неё пробелы
|
// Нормализуем строку и вырежем из неё пробелы.
|
||||||
normPhrase := strings.ReplaceAll(nString(v.Text), " ", "")
|
normPhrase := strings.ReplaceAll(nString(v.Text), " ", "")
|
||||||
|
|
||||||
// Проверяем согласно тому, что длина фразы более чем сколько-то символов
|
// Проверяем согласно тому, что длина фразы более чем сколько-то символов.
|
||||||
if len(normPhrase) >= channel.AllCaps.MinLength {
|
if len(normPhrase) >= channel.AllCaps.MinLength {
|
||||||
normPhraseUpper := strings.ReplaceAll(nStringUpper(v.Text), " ", "")
|
normPhraseUpper := strings.ReplaceAll(nStringUpper(v.Text), " ", "")
|
||||||
|
|
||||||
@@ -401,53 +401,81 @@ func (j *Jabber) BunySoftwareVersion(v xmpp.IQ, ver IqResultSoftwareVersion) err
|
|||||||
var err error
|
var err error
|
||||||
|
|
||||||
if v.From != "" {
|
if v.From != "" {
|
||||||
// На всякий случай: себя никогда не баним, явным образом
|
// На всякий случай: себя никогда не баним, явным образом.
|
||||||
if v.From == j.Talk.JID() {
|
if v.From == j.Talk.JID() {
|
||||||
|
log.Debugf("Skipping useragent blacklist check for myself")
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
room := strings.SplitN(v.From, "/", 2)[0]
|
room := strings.SplitN(v.From, "/", 2)[0]
|
||||||
|
|
||||||
var (
|
|
||||||
presenceJSONStrings []string
|
|
||||||
)
|
|
||||||
|
|
||||||
if presenceJSONInterface, present := j.RoomPresences.Get(room); present {
|
if presenceJSONInterface, present := j.RoomPresences.Get(room); present {
|
||||||
presenceJSONStrings = InterfaceToStringSlice(presenceJSONInterface)
|
presenceJSON, ok := presenceJSONInterface.([]byte)
|
||||||
}
|
|
||||||
|
|
||||||
log.Debugf("Looking up %s in presence db", v.From)
|
if !ok {
|
||||||
|
log.Errorf("Looks like we have corrupted entry in presence db")
|
||||||
|
|
||||||
for _, presenceJSONString := range presenceJSONStrings {
|
return nil
|
||||||
var p xmpp.Presence
|
|
||||||
_ = json.Unmarshal([]byte(presenceJSONString), &p)
|
|
||||||
|
|
||||||
if p.Affiliation == "admin" || p.Affiliation == "owner" {
|
|
||||||
log.Infof("Skipping useragent blacklist check for %s because his affiliation is %s", v.From, p.Affiliation)
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// На всякий случай.
|
var presences []xmpp.Presence
|
||||||
if p.Role == "moderator" {
|
|
||||||
log.Infof("Skipping useragent blacklist check for %s because his role is %s", v.From, p.Role)
|
|
||||||
|
|
||||||
continue
|
if err := json.Unmarshal(presenceJSON, &presences); err != nil {
|
||||||
|
log.Errorf("Looks like we have corrupted entry in presence db")
|
||||||
|
|
||||||
|
return nil //nolint: nilerr // Это не критическая ошибка, её не надо возвращать.
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.From == p.From {
|
for _, p := range presences { //nolint: gocritic // Развели тут, блять, страну советов, напиши что надо сделать кодом, сука.
|
||||||
log.Errorf("Found jid of %s in presence db: %s", v.From, p.JID)
|
// Если находим, что у нас уже есть клиент с таким же From, то есть полным nick-ом (для groupchat)
|
||||||
|
// пропускаем его.
|
||||||
|
if p.From != v.From {
|
||||||
|
log.Debugf("Skip check for %s because it is not %s", p.From, v.From)
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if j.C.Debug.PresenceDB {
|
||||||
|
log.Debugf("Found jid of %s in presence db: %s", v.From, p.JID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.Affiliation == "admin" || p.Affiliation == "owner" {
|
||||||
|
log.Infof("Skipping useragent blacklist check for %s because his affiliation is %s", v.From, p.Affiliation)
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// На всякий случай.
|
||||||
|
if p.Role == "moderator" {
|
||||||
|
log.Infof("Skipping useragent blacklist check for %s because his role is %s", v.From, p.Role)
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug("Useragent is not owner or moderator, proceeding with check...")
|
||||||
|
|
||||||
// Lookup in global blacklist.
|
// Lookup in global blacklist.
|
||||||
|
log.Debug("Searching our room in connected room list...")
|
||||||
|
|
||||||
for _, cRoom := range j.RoomsConnected {
|
for _, cRoom := range j.RoomsConnected {
|
||||||
if cRoom == room {
|
if cRoom == room {
|
||||||
|
log.Debug("Found our room in list of connected rooms")
|
||||||
|
|
||||||
for _, bEntry := range j.BlackList.Blacklist {
|
for _, bEntry := range j.BlackList.Blacklist {
|
||||||
// Обработаем правила глобального чёрного списка
|
// Обработаем правила глобального чёрного списка.
|
||||||
if bEntry.RoomName == "" {
|
if bEntry.RoomName == "" {
|
||||||
|
log.Debugf("Check in global blacklist")
|
||||||
|
log.Debugf("We have useragent struct with Name=%s, Version=%s, OS=%s", ver.Name, ver.Version, ver.Os)
|
||||||
|
|
||||||
for _, useragent := range bEntry.UserAgent {
|
for _, useragent := range bEntry.UserAgent {
|
||||||
|
log.Debugf("We have useragent blacklist entry Name=%s, Version=%s, OS=%s", useragent.Name, useragent.Version, useragent.Os)
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case useragent.Name != "" && useragent.Version != "" && useragent.Os != "":
|
case useragent.Name != "" && useragent.Version != "" && useragent.Os != "":
|
||||||
if ver.Name == useragent.Name && ver.Version == useragent.Version && ver.Os == useragent.Os {
|
if ver.Name == useragent.Name && ver.Version == useragent.Version && ver.Os == useragent.Os {
|
||||||
|
log.Warnf("Hammer falls on %s (%s): Software Name, Version and OS match entry in blacklist", p.From, p.JID)
|
||||||
|
|
||||||
id, err := j.Squash(room, p.JID, bEntry.ReasonEnable, v.Type)
|
id, err := j.Squash(room, p.JID, bEntry.ReasonEnable, v.Type)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -463,8 +491,10 @@ func (j *Jabber) BunySoftwareVersion(v xmpp.IQ, ver IqResultSoftwareVersion) err
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
case useragent.Name != "" && useragent.Version != "":
|
case useragent.Name != "" && useragent.Version != "" && useragent.Os == "":
|
||||||
if ver.Name == useragent.Name && ver.Version == useragent.Version {
|
if ver.Name == useragent.Name && ver.Version == useragent.Version {
|
||||||
|
log.Warnf("Hammer falls on %s (%s): Software Name and Version match entry in blacklist", p.From, p.JID)
|
||||||
|
|
||||||
id, err := j.Squash(room, p.JID, bEntry.ReasonEnable, v.Type)
|
id, err := j.Squash(room, p.JID, bEntry.ReasonEnable, v.Type)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -480,8 +510,10 @@ func (j *Jabber) BunySoftwareVersion(v xmpp.IQ, ver IqResultSoftwareVersion) err
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
case useragent.Name != "":
|
case useragent.Name != "" && useragent.Version == "" && useragent.Os == "":
|
||||||
if ver.Name == useragent.Name {
|
if ver.Name == useragent.Name {
|
||||||
|
log.Warnf("Hammer falls on %s (%s): Software Name match entry in blacklist", p.From, p.JID)
|
||||||
|
|
||||||
id, err := j.Squash(room, p.JID, bEntry.ReasonEnable, v.Type)
|
id, err := j.Squash(room, p.JID, bEntry.ReasonEnable, v.Type)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -497,8 +529,10 @@ func (j *Jabber) BunySoftwareVersion(v xmpp.IQ, ver IqResultSoftwareVersion) err
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
case useragent.Version != "":
|
case useragent.Name == "" && useragent.Version != "" && useragent.Os == "":
|
||||||
if ver.Version == useragent.Version {
|
if ver.Version == useragent.Version {
|
||||||
|
log.Warnf("Hammer falls on %s (%s): Software Version match entry in blacklist", p.From, p.JID)
|
||||||
|
|
||||||
id, err := j.Squash(room, p.JID, bEntry.ReasonEnable, v.Type)
|
id, err := j.Squash(room, p.JID, bEntry.ReasonEnable, v.Type)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -515,13 +549,21 @@ func (j *Jabber) BunySoftwareVersion(v xmpp.IQ, ver IqResultSoftwareVersion) err
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Debug("No matches in global software versions blacklist")
|
||||||
}
|
}
|
||||||
|
|
||||||
if bEntry.RoomName == room {
|
if bEntry.RoomName == room {
|
||||||
|
log.Debug("Found blacklist entries for room")
|
||||||
|
|
||||||
for _, useragent := range bEntry.UserAgent {
|
for _, useragent := range bEntry.UserAgent {
|
||||||
|
log.Debugf("We have useragent blacklist entry Name=%s, Version=%s, OS=%s", useragent.Name, useragent.Version, useragent.Os)
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case useragent.Name != "" && useragent.Version != "" && useragent.Os != "":
|
case useragent.Name != "" && useragent.Version != "" && useragent.Os != "":
|
||||||
if ver.Name == useragent.Name && ver.Version == useragent.Version && ver.Os == useragent.Os {
|
if ver.Name == useragent.Name && ver.Version == useragent.Version && ver.Os == useragent.Os {
|
||||||
|
log.Warnf("Hammer falls on %s (%s): Software Name, Version and OS match entry in blacklist", p.From, p.JID)
|
||||||
|
|
||||||
id, err := j.Squash(room, p.JID, bEntry.ReasonEnable, v.Type)
|
id, err := j.Squash(room, p.JID, bEntry.ReasonEnable, v.Type)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -537,8 +579,10 @@ func (j *Jabber) BunySoftwareVersion(v xmpp.IQ, ver IqResultSoftwareVersion) err
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
case useragent.Name != "" && useragent.Version != "":
|
case useragent.Name != "" && useragent.Version != "" && useragent.Os == "":
|
||||||
if ver.Name == useragent.Name && ver.Version == useragent.Version {
|
if ver.Name == useragent.Name && ver.Version == useragent.Version {
|
||||||
|
log.Warnf("Hammer falls on %s (%s): Software Name and Version match entry in blacklist", p.From, p.JID)
|
||||||
|
|
||||||
id, err := j.Squash(room, p.JID, bEntry.ReasonEnable, v.Type)
|
id, err := j.Squash(room, p.JID, bEntry.ReasonEnable, v.Type)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -554,8 +598,10 @@ func (j *Jabber) BunySoftwareVersion(v xmpp.IQ, ver IqResultSoftwareVersion) err
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
case useragent.Name != "":
|
case useragent.Name != "" && useragent.Version == "" && useragent.Os == "":
|
||||||
if ver.Name == useragent.Name {
|
if ver.Name == useragent.Name {
|
||||||
|
log.Warnf("Hammer falls on %s (%s): Software Name match entry in blacklist", p.From, p.JID)
|
||||||
|
|
||||||
id, err := j.Squash(room, p.JID, bEntry.ReasonEnable, v.Type)
|
id, err := j.Squash(room, p.JID, bEntry.ReasonEnable, v.Type)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -571,8 +617,10 @@ func (j *Jabber) BunySoftwareVersion(v xmpp.IQ, ver IqResultSoftwareVersion) err
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
case useragent.Version != "":
|
case useragent.Name == "" && useragent.Version != "" && useragent.Os == "":
|
||||||
if ver.Version == useragent.Version {
|
if ver.Version == useragent.Version {
|
||||||
|
log.Warnf("Hammer falls on %s (%s): Software Version match entry in blacklist", p.From, p.JID)
|
||||||
|
|
||||||
id, err := j.Squash(room, p.JID, bEntry.ReasonEnable, v.Type)
|
id, err := j.Squash(room, p.JID, bEntry.ReasonEnable, v.Type)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -589,6 +637,8 @@ func (j *Jabber) BunySoftwareVersion(v xmpp.IQ, ver IqResultSoftwareVersion) err
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Debugf("No matches in software versions blacklist for %s", room)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,16 +4,18 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Collection это sync.Map-ка, хранящая в себе произвольные итемы.
|
type (
|
||||||
type Collection struct {
|
// Collection это sync.Map-ка, хранящая в себе произвольные итемы.
|
||||||
items sync.Map
|
Collection struct {
|
||||||
close chan struct{}
|
close chan struct{}
|
||||||
}
|
items sync.Map
|
||||||
|
}
|
||||||
|
|
||||||
// Item представляет собой интерфейс для хранения произвольных данных.
|
// Item представляет собой интерфейс для хранения произвольных данных.
|
||||||
type item struct {
|
item struct {
|
||||||
data interface{}
|
data any
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// NewCollection создаёт инстанс коллекции.
|
// NewCollection создаёт инстанс коллекции.
|
||||||
func NewCollection() *Collection {
|
func NewCollection() *Collection {
|
||||||
@@ -25,7 +27,7 @@ func NewCollection() *Collection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get достаёт данные по заданному ключу из коллекции.
|
// Get достаёт данные по заданному ключу из коллекции.
|
||||||
func (collection *Collection) Get(key interface{}) (interface{}, bool) {
|
func (collection *Collection) Get(key any) (any, bool) {
|
||||||
obj, exists := collection.items.Load(key)
|
obj, exists := collection.items.Load(key)
|
||||||
|
|
||||||
if !exists {
|
if !exists {
|
||||||
@@ -38,15 +40,15 @@ func (collection *Collection) Get(key interface{}) (interface{}, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set сохраняет данные с заданным ключом в коллекцию.
|
// Set сохраняет данные с заданным ключом в коллекцию.
|
||||||
func (collection *Collection) Set(key interface{}, value interface{}) {
|
func (collection *Collection) Set(key any, value any) {
|
||||||
collection.items.Store(key, item{
|
collection.items.Store(key, item{
|
||||||
data: value,
|
data: value,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Range применяет функцию f ко всем ключам в коллекции.
|
// Range применяет функцию f ко всем ключам в коллекции.
|
||||||
func (collection *Collection) Range(f func(key, value interface{}) bool) {
|
func (collection *Collection) Range(f func(key, value any) bool) {
|
||||||
fn := func(key, value interface{}) bool {
|
fn := func(key, value any) bool {
|
||||||
item := value.(item)
|
item := value.(item)
|
||||||
|
|
||||||
return f(key, item.data)
|
return f(key, item.data)
|
||||||
@@ -56,13 +58,14 @@ func (collection *Collection) Range(f func(key, value interface{}) bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Delete удаляет ключ и значение из коллекции данных.
|
// Delete удаляет ключ и значение из коллекции данных.
|
||||||
func (collection *Collection) Delete(key interface{}) {
|
func (collection *Collection) Delete(key any) {
|
||||||
collection.items.Delete(key)
|
collection.items.Delete(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close очищает и высвобождает ресурсы, занятые коллекцией.
|
// Close очищает и высвобождает ресурсы, занятые коллекцией.
|
||||||
func (collection *Collection) Close() {
|
func (collection *Collection) Close() {
|
||||||
collection.close <- struct{}{}
|
collection.close <- struct{}{}
|
||||||
|
|
||||||
collection.items = sync.Map{}
|
collection.items = sync.Map{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
package jabber
|
package jabber
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"buny-jabber-bot/internal/log"
|
||||||
"fmt"
|
"fmt"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
"github.com/eleksir/go-xmpp"
|
"github.com/xmppo/go-xmpp"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Cmd парсит команды из чятика.
|
// Cmd парсит команды из чятика.
|
||||||
func (j *Jabber) Cmd(v xmpp.Chat) error {
|
func (j *Jabber) Cmd(v xmpp.Chat) error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
switch {
|
switch v.Text {
|
||||||
case v.Text == fmt.Sprintf("%shelp", j.C.CSign) || v.Text == fmt.Sprintf("%sпомощь", j.C.CSign):
|
case j.C.CSign + "help", j.C.CSign + "помощь":
|
||||||
var (
|
var (
|
||||||
answer string
|
answer string
|
||||||
chosenOneTalks = false
|
chosenOneTalks = false
|
||||||
@@ -30,10 +30,11 @@ func (j *Jabber) Cmd(v xmpp.Chat) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if chosenOneTalks {
|
if chosenOneTalks {
|
||||||
answer = fmt.Sprintf("%sпомощь - этот список команд\n", j.C.CSign)
|
answer = j.C.CSign + "помощь - этот список команд\n"
|
||||||
answer += fmt.Sprintf("%shelp - this commands list\n", j.C.CSign)
|
|
||||||
answer += fmt.Sprintf("%srehash - reload white and black lists (available to bot admins only)\n", j.C.CSign)
|
answer += j.C.CSign + "help - this commands list\n"
|
||||||
answer += fmt.Sprintf("%sver|%sversion - prints version of software", j.C.CSign, j.C.CSign)
|
answer += j.C.CSign + "rehash - reload white and black lists (available to bot admins only)\n"
|
||||||
|
answer += j.C.CSign + "ver | " + j.C.CSign + "version - prints version of software"
|
||||||
} else {
|
} else {
|
||||||
answer = "Ничем помочь не могу. Луна не светит на тебя."
|
answer = "Ничем помочь не могу. Луна не светит на тебя."
|
||||||
}
|
}
|
||||||
@@ -56,15 +57,14 @@ func (j *Jabber) Cmd(v xmpp.Chat) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
case v.Text == fmt.Sprintf("%srehash", j.C.CSign): //nolint:wsl
|
case j.C.CSign + "rehash":
|
||||||
/* Эта команда может прилетать из приватной беседы с realjid-ом, из приватной беседы с chat-nick-ом, а также из
|
/* Эта команда может прилетать из приватной беседы с realjid-ом, из приватной беседы с chat-nick-ом, а также из
|
||||||
* чятика.
|
* чятика.
|
||||||
* Groupchat мы можем определить по v.Type (groupchat) и тогда понятно что делать.
|
* Groupchat мы можем определить по v.Type (groupchat) и тогда понятно что делать.
|
||||||
* А вот, например, приватные притязания определить несколько сложно-вато. Но можно попробовать, например,
|
* А вот, например, приватные притязания определить несколько сложно-вато. Но можно попробовать, например,
|
||||||
* пытаться по списку presence-ов находить персонажа и его real jid и уже из этого определять master он или нет.
|
* пытаться по списку presence-ов находить персонажа и его real jid и уже из этого определять master он или нет.
|
||||||
*/
|
*/
|
||||||
|
// Попробуем определить каков алгоритм наших действий, в зависимости от типа сообщения.
|
||||||
// Попробуем определить каков алгоритм наших действий, в зависимости от типа сообщения
|
|
||||||
switch v.Type {
|
switch v.Type {
|
||||||
case "groupchat":
|
case "groupchat":
|
||||||
// Проверим, является ли "командир" избранным или это самозванец?
|
// Проверим, является ли "командир" избранным или это самозванец?
|
||||||
@@ -90,6 +90,7 @@ func (j *Jabber) Cmd(v xmpp.Chat) error {
|
|||||||
)
|
)
|
||||||
|
|
||||||
var msg xmpp.Chat
|
var msg xmpp.Chat
|
||||||
|
|
||||||
msg.Remote = v.Remote
|
msg.Remote = v.Remote
|
||||||
msg.Type = v.Type
|
msg.Type = v.Type
|
||||||
msg.Text = "Ничем помочь не могу. Луна не светит на тебя."
|
msg.Text = "Ничем помочь не могу. Луна не светит на тебя."
|
||||||
@@ -103,8 +104,9 @@ func (j *Jabber) Cmd(v xmpp.Chat) error {
|
|||||||
listsLoaded = false
|
listsLoaded = false
|
||||||
|
|
||||||
var msg xmpp.Chat
|
var msg xmpp.Chat
|
||||||
|
|
||||||
msg.Remote = v.Remote
|
msg.Remote = v.Remote
|
||||||
msg.Type = "chat" // private message
|
msg.Type = "chat" // private message.
|
||||||
msg.Text = fmt.Sprint(err)
|
msg.Text = fmt.Sprint(err)
|
||||||
|
|
||||||
if _, err := j.Talk.Send(msg); err != nil {
|
if _, err := j.Talk.Send(msg); err != nil {
|
||||||
@@ -116,8 +118,9 @@ func (j *Jabber) Cmd(v xmpp.Chat) error {
|
|||||||
listsLoaded = false
|
listsLoaded = false
|
||||||
|
|
||||||
var msg xmpp.Chat
|
var msg xmpp.Chat
|
||||||
|
|
||||||
msg.Remote = v.Remote
|
msg.Remote = v.Remote
|
||||||
msg.Type = "chat" // private message
|
msg.Type = "chat" // private message.
|
||||||
msg.Text = fmt.Sprint(err)
|
msg.Text = fmt.Sprint(err)
|
||||||
|
|
||||||
if _, err := j.Talk.Send(msg); err != nil {
|
if _, err := j.Talk.Send(msg); err != nil {
|
||||||
@@ -129,6 +132,7 @@ func (j *Jabber) Cmd(v xmpp.Chat) error {
|
|||||||
room := (strings.SplitN(v.Remote, "/", 2))[0]
|
room := (strings.SplitN(v.Remote, "/", 2))[0]
|
||||||
|
|
||||||
var msg xmpp.Chat
|
var msg xmpp.Chat
|
||||||
|
|
||||||
msg.Remote = room
|
msg.Remote = room
|
||||||
msg.Type = v.Type
|
msg.Type = v.Type
|
||||||
msg.Text = "Сделано"
|
msg.Text = "Сделано"
|
||||||
@@ -137,17 +141,18 @@ func (j *Jabber) Cmd(v xmpp.Chat) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case "chat":
|
case "chat":
|
||||||
room := (strings.SplitN(v.Remote, "/", 2))[0]
|
room := (strings.SplitN(v.Remote, "/", 2))[0]
|
||||||
|
|
||||||
// Реагировать на приватную команду "из комнаты", только если бот в комнате
|
// Реагировать на приватную команду "из комнаты", только если бот в комнате.
|
||||||
if slices.Contains(j.RoomsConnected, room) {
|
if slices.Contains(j.RoomsConnected, room) {
|
||||||
var (
|
var (
|
||||||
chosenOneTalks = false
|
chosenOneTalks = false
|
||||||
listsLoaded = true
|
listsLoaded = true
|
||||||
)
|
)
|
||||||
|
|
||||||
// Реагировать на rehash только если собеседник является bot master-ом
|
// Реагировать на rehash только если собеседник является bot master-ом.
|
||||||
realJID := j.GetRealJIDfromNick(v.Remote)
|
realJID := j.GetRealJIDfromNick(v.Remote)
|
||||||
|
|
||||||
for _, master := range j.C.Jabber.BotMasters {
|
for _, master := range j.C.Jabber.BotMasters {
|
||||||
@@ -165,6 +170,7 @@ func (j *Jabber) Cmd(v xmpp.Chat) error {
|
|||||||
)
|
)
|
||||||
|
|
||||||
var msg xmpp.Chat
|
var msg xmpp.Chat
|
||||||
|
|
||||||
msg.Remote = v.Remote
|
msg.Remote = v.Remote
|
||||||
msg.Type = v.Type
|
msg.Type = v.Type
|
||||||
msg.Text = "Ничем помочь не могу. Луна не светит на тебя."
|
msg.Text = "Ничем помочь не могу. Луна не светит на тебя."
|
||||||
@@ -178,6 +184,7 @@ func (j *Jabber) Cmd(v xmpp.Chat) error {
|
|||||||
listsLoaded = false
|
listsLoaded = false
|
||||||
|
|
||||||
var msg xmpp.Chat
|
var msg xmpp.Chat
|
||||||
|
|
||||||
msg.Remote = v.Remote
|
msg.Remote = v.Remote
|
||||||
msg.Type = v.Type
|
msg.Type = v.Type
|
||||||
msg.Text = fmt.Sprint(err)
|
msg.Text = fmt.Sprint(err)
|
||||||
@@ -191,6 +198,7 @@ func (j *Jabber) Cmd(v xmpp.Chat) error {
|
|||||||
listsLoaded = false
|
listsLoaded = false
|
||||||
|
|
||||||
var msg xmpp.Chat
|
var msg xmpp.Chat
|
||||||
|
|
||||||
msg.Remote = v.Remote
|
msg.Remote = v.Remote
|
||||||
msg.Type = v.Type
|
msg.Type = v.Type
|
||||||
msg.Text = fmt.Sprint(err)
|
msg.Text = fmt.Sprint(err)
|
||||||
@@ -202,6 +210,7 @@ func (j *Jabber) Cmd(v xmpp.Chat) error {
|
|||||||
|
|
||||||
if listsLoaded {
|
if listsLoaded {
|
||||||
var msg xmpp.Chat
|
var msg xmpp.Chat
|
||||||
|
|
||||||
msg.Remote = v.Remote
|
msg.Remote = v.Remote
|
||||||
msg.Type = v.Type
|
msg.Type = v.Type
|
||||||
msg.Text = "Сделано"
|
msg.Text = "Сделано"
|
||||||
@@ -213,13 +222,19 @@ func (j *Jabber) Cmd(v xmpp.Chat) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
log.Infof("Rehash command from outer space: %s", spew.Sdump(v))
|
msg := "Rehash command from outer space: " + spew.Sdump(v)
|
||||||
|
|
||||||
|
for str := range strings.SplitSeq(msg, "\n") {
|
||||||
|
log.Info(str)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
case v.Text == fmt.Sprintf("%sver", j.C.CSign) || v.Text == fmt.Sprintf("%sversion", j.C.CSign):
|
|
||||||
|
case j.C.CSign + "ver", j.C.CSign + "version":
|
||||||
var (
|
var (
|
||||||
answer = fmt.Sprintf("Version %s", j.C.Version)
|
answer = "Version " + j.C.Version
|
||||||
)
|
)
|
||||||
|
|
||||||
dest := v.Remote
|
dest := v.Remote
|
||||||
|
|||||||
@@ -1,37 +1,35 @@
|
|||||||
package jabber
|
package jabber
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"buny-jabber-bot/internal/log"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"fmt"
|
"fmt"
|
||||||
"runtime"
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
"github.com/eleksir/go-xmpp"
|
"github.com/xmppo/go-xmpp"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"golang.org/x/exp/slices"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ParseEvent парсит ивенты, прилетающие из модуля xmpp (изменения presence, фразы участников, ошибки).
|
// ParseEvent парсит ивенты, прилетающие из модуля xmpp (изменения presence, фразы участников, ошибки).
|
||||||
func (j *Jabber) ParseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
|
func (j *Jabber) ParseEvent(e any) { //nolint:maintidx,gocognit,gocyclo
|
||||||
j.LastServerActivity = time.Now().Unix()
|
j.LastServerActivity = time.Now().Unix()
|
||||||
|
|
||||||
switch v := e.(type) {
|
switch v := e.(type) {
|
||||||
// Сообщение в чяти
|
// Сообщение в чяти.
|
||||||
case xmpp.Chat:
|
case xmpp.Chat:
|
||||||
log.Debugf("Looks like message, ChatType: %s, From: %s, Subject: %s Text: %s",
|
log.Debugf("Looks like message, ChatType: %s, From: %s, Subject: %s Text: %s",
|
||||||
v.Type, v.Remote, v.Subject, v.Text)
|
v.Type, v.Remote, v.Subject, v.Text)
|
||||||
|
|
||||||
// Топик чятика присылается в виде сообщения с subject, но без text
|
// Топик чятика присылается в виде сообщения с subject, но без text.
|
||||||
// В то же время сообщения от людей приходят с пустым subject, но с заполненным text
|
// В то же время сообщения от людей приходят с пустым subject, но с заполненным text.
|
||||||
if v.Text != "" {
|
if v.Text != "" {
|
||||||
// Чятики бывают групповые и не групповые, от этого зависит Remote, куда направлять сообщение
|
// Чятики бывают групповые и не групповые, от этого зависит Remote, куда направлять сообщение.
|
||||||
switch v.Type { //nolint:wsl,whitespace
|
switch v.Type { //nolint:wsl,whitespace
|
||||||
|
// Групповой чятик.
|
||||||
// Групповой чятик
|
|
||||||
case "groupchat":
|
case "groupchat":
|
||||||
var (
|
var (
|
||||||
room = strings.SplitN(v.Remote, "/", 2)[0]
|
room = strings.SplitN(v.Remote, "/", 2)[0]
|
||||||
@@ -64,7 +62,7 @@ func (j *Jabber) ParseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
|
|||||||
j.LastMucActivity.Set(muc, j.LastServerActivity)
|
j.LastMucActivity.Set(muc, j.LastServerActivity)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Приватный чятик
|
// Приватный чятик.
|
||||||
case "chat":
|
case "chat":
|
||||||
// Здесь у нас может быть 2 вида From:
|
// Здесь у нас может быть 2 вида From:
|
||||||
// slackware-current@conference.jabber.ru/eleksir - это если сообщение из публичного чятика
|
// slackware-current@conference.jabber.ru/eleksir - это если сообщение из публичного чятика
|
||||||
@@ -80,17 +78,19 @@ func (j *Jabber) ParseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
|
|||||||
j.LastActivity = j.LastServerActivity
|
j.LastActivity = j.LastServerActivity
|
||||||
|
|
||||||
// Внезапно, ошибки. По идее они должны ассоциироваться с отправляемыми сообщениями, но по ходу это
|
// Внезапно, ошибки. По идее они должны ассоциироваться с отправляемыми сообщениями, но по ходу это
|
||||||
// не реализовано, поэтому мы получаем ошибки в форме отдельного чятика
|
// не реализовано, поэтому мы получаем ошибки в форме отдельного чятика.
|
||||||
case "error":
|
case "error":
|
||||||
// Это ошибка со стороны сервера, видимо, в ответ на наши действия и поскольку такие вещи происходят
|
// Это ошибка со стороны сервера, видимо, в ответ на наши действия и поскольку такие вещи происходят
|
||||||
// асинхронно, связать их с конкретными нашими действиями довольно сложно.
|
// асинхронно, связать их с конкретными нашими действиями довольно сложно.
|
||||||
log.Warn(spew.Sdump(e))
|
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
|
||||||
|
log.Error(str)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// IQ-сообщение - пинг, понг, что-то ещё...
|
// IQ-сообщение - пинг, понг, что-то ещё...
|
||||||
case xmpp.IQ:
|
case xmpp.IQ:
|
||||||
// По правилам IQ обязательно должна содержать ID, причём не пустой
|
// По правилам IQ обязательно должна содержать ID, причём не пустой.
|
||||||
if v.ID == "" {
|
if v.ID == "" {
|
||||||
log.Info("Got an IQ stanza with empty id, discarding")
|
log.Info("Got an IQ stanza with empty id, discarding")
|
||||||
|
|
||||||
@@ -114,72 +114,12 @@ func (j *Jabber) ParseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
|
|||||||
|
|
||||||
if err := xml.Unmarshal(v.Query, &iqStruct); err == nil {
|
if err := xml.Unmarshal(v.Query, &iqStruct); err == nil {
|
||||||
switch iqStruct.Xmlns { //nolint:wsl,whitespace
|
switch iqStruct.Xmlns { //nolint:wsl,whitespace
|
||||||
|
// Запрос на список поддерживаемых фич - версия, локальное время, итд.
|
||||||
// Запрос номера версии приложения
|
|
||||||
case "jabber:iq:version":
|
|
||||||
log.Infof("Got IQ get request for version from %s", v.From)
|
|
||||||
|
|
||||||
if id, err := j.Talk.IqVersionResponse(
|
|
||||||
v,
|
|
||||||
j.C.ExeName,
|
|
||||||
j.C.Version,
|
|
||||||
(strings.SplitN(runtime.GOOS, "/", 2))[0],
|
|
||||||
); err != nil {
|
|
||||||
err := fmt.Errorf(
|
|
||||||
"unable to send version info to jabber server: id=%s, err=%w",
|
|
||||||
id,
|
|
||||||
err,
|
|
||||||
)
|
|
||||||
|
|
||||||
j.GTomb.Kill(err)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
parseError = false
|
|
||||||
|
|
||||||
// Нам прислали попингуй
|
|
||||||
case "urn:xmpp:ping":
|
|
||||||
log.Infof("Got IQ get request for pong from %s", v.From)
|
|
||||||
|
|
||||||
if id, err := j.Talk.PingResponse(v); err != nil {
|
|
||||||
err := fmt.Errorf(
|
|
||||||
"unable to send pong to jabber server: id=%s, err=%w",
|
|
||||||
id,
|
|
||||||
err,
|
|
||||||
)
|
|
||||||
|
|
||||||
j.GTomb.Kill(err)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
parseError = false
|
|
||||||
|
|
||||||
// У нас запросили время последней активности. В нашем случае это либо время запуска клиента, либо время
|
|
||||||
// последней фразы в чяти. Xep-0012.
|
|
||||||
case "jabber:iq:last":
|
|
||||||
log.Infof("Got IQ get last activity time request from %s", v.From)
|
|
||||||
|
|
||||||
if id, err := j.Talk.JabberIqLastResponse(v, j.LastActivity); err != nil {
|
|
||||||
err := fmt.Errorf(
|
|
||||||
"unable to send last activity time to jabber server: id=%s, err=%w",
|
|
||||||
id,
|
|
||||||
err,
|
|
||||||
)
|
|
||||||
|
|
||||||
j.GTomb.Kill(err)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
parseError = false
|
|
||||||
|
|
||||||
// Запрос на список поддерживаемых фич - версия, локальное время, итд
|
|
||||||
case xmpp.XMPPNS_DISCO_INFO:
|
case xmpp.XMPPNS_DISCO_INFO:
|
||||||
log.Infof("Got IQ get disco#info request from %s", v.From)
|
log.Infof("Got IQ get disco#info request from %s", v.From)
|
||||||
|
|
||||||
answer := "<query xmlns=\"http://jabber.org/protocol/disco#info\">"
|
answer := "<query xmlns=\"http://jabber.org/protocol/disco#info\">"
|
||||||
|
|
||||||
answer += "<feature var=\"jabber:iq:version\" />"
|
answer += "<feature var=\"jabber:iq:version\" />"
|
||||||
answer += "<feature var=\"urn:xmpp:time\" />"
|
answer += "<feature var=\"urn:xmpp:time\" />"
|
||||||
answer += "<feature var=\"urn:xmpp:ping\" />"
|
answer += "<feature var=\"urn:xmpp:ping\" />"
|
||||||
@@ -209,7 +149,7 @@ func (j *Jabber) ParseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
|
|||||||
|
|
||||||
parseError = false
|
parseError = false
|
||||||
|
|
||||||
// Спрашивают список команд, чтобы поуправлять этим клиентом (xep-0030)
|
// Спрашивают список команд, чтобы поуправлять этим клиентом (xep-0030).
|
||||||
case xmpp.XMPPNS_DISCO_ITEMS:
|
case xmpp.XMPPNS_DISCO_ITEMS:
|
||||||
log.Infof("Got IQ get disco#items request from %s, answer service unavailable", v.From)
|
log.Infof("Got IQ get disco#items request from %s, answer service unavailable", v.From)
|
||||||
|
|
||||||
@@ -231,10 +171,13 @@ func (j *Jabber) ParseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
|
|||||||
|
|
||||||
parseError = false
|
parseError = false
|
||||||
|
|
||||||
// Запрашивают что-то, о чём мы не имеем представления
|
// Запрашивают что-то, о чём мы не имеем представления.
|
||||||
default:
|
default:
|
||||||
log.Info("Got an unknown IQ get request, discarding")
|
log.Info("Got an unknown IQ get request, discarding")
|
||||||
log.Info(spew.Sdump(e))
|
|
||||||
|
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
|
||||||
|
log.Debug(str)
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -242,7 +185,7 @@ func (j *Jabber) ParseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
|
|||||||
parseError = true
|
parseError = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Попробуем распарсить входящий запрос как urn:xmpp:time
|
// Попробуем распарсить входящий запрос как urn:xmpp:time.
|
||||||
if parseError {
|
if parseError {
|
||||||
var iqStruct TimeIqGetQuery
|
var iqStruct TimeIqGetQuery
|
||||||
|
|
||||||
@@ -267,7 +210,10 @@ func (j *Jabber) ParseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
log.Info("Got bogus IQ get time request, discarding")
|
log.Info("Got bogus IQ get time request, discarding")
|
||||||
log.Info(spew.Sdump(e))
|
|
||||||
|
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
|
||||||
|
log.Debug(str)
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -276,7 +222,7 @@ func (j *Jabber) ParseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Попробуем распарсить входящий запрос как http://jabber.org/protocol/pubsub
|
// Попробуем распарсить входящий запрос как http://jabber.org/protocol/pubsub.
|
||||||
if parseError {
|
if parseError {
|
||||||
var iqStruct PubsubIQGetQuery
|
var iqStruct PubsubIQGetQuery
|
||||||
|
|
||||||
@@ -304,7 +250,11 @@ func (j *Jabber) ParseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
log.Info("Got unknown IQ get pubsub something request, discarding")
|
log.Info("Got unknown IQ get pubsub something request, discarding")
|
||||||
log.Info(spew.Sdump(e))
|
|
||||||
|
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
|
||||||
|
log.Debug(str)
|
||||||
|
}
|
||||||
|
|
||||||
parseError = true //nolint:wsl
|
parseError = true //nolint:wsl
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -312,35 +262,13 @@ func (j *Jabber) ParseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Попробуем распарсить входящий запрос как urn:xmpp:ping он приходит при MUC Self-Ping (Schrödinger's Chat)
|
// Не знаю, как парсить... залоггируем это дело и пойдём дальше.
|
||||||
// Предполагается, что такие респонсы должны приходить только для пинга без серверной оптимизации.
|
|
||||||
// Хотя идейно мы не поддерживаем работу без серверной оптимизации, но на пинг ответим, нам несложно.
|
|
||||||
if parseError {
|
|
||||||
var iqStruct IqPing
|
|
||||||
|
|
||||||
if err := xml.Unmarshal(v.Query, &iqStruct); err == nil {
|
|
||||||
log.Debugf("Got IQ get request (actually, response) for MUC Self-Ping from %s", v.From)
|
|
||||||
|
|
||||||
if id, err := j.Talk.PingResponse(v); err != nil {
|
|
||||||
err := fmt.Errorf(
|
|
||||||
"unable to send pong to jabber server: id=%s, err=%w",
|
|
||||||
id,
|
|
||||||
err,
|
|
||||||
)
|
|
||||||
|
|
||||||
j.GTomb.Kill(err)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
parseError = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Не знаю, как парсить... залоггируем это дело и пойдём дальше
|
|
||||||
if parseError {
|
if parseError {
|
||||||
log.Infof("Does not look like parsable via iqStruct")
|
log.Infof("Does not look like parsable via iqStruct")
|
||||||
log.Info(spew.Sdump(e))
|
|
||||||
|
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
|
||||||
|
log.Debug(str)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case xmpp.IQTypeResult:
|
case xmpp.IQTypeResult:
|
||||||
@@ -351,9 +279,10 @@ func (j *Jabber) ParseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
// Похоже на pong от сервера (по стандарту в ответе нету query, но go-xmpp нам подсовывает это)
|
// Похоже на pong от сервера (по стандарту в ответе нету query, но go-xmpp нам подсовывает это).
|
||||||
case v.From == j.C.Jabber.Server && v.To == j.Talk.JID() && string(v.Query) == "<XMLElement></XMLElement>":
|
case v.From == j.C.Jabber.Server && v.To == j.Talk.JID() && string(v.Query) == "<XMLElement></XMLElement>":
|
||||||
log.Debugf("Got S2C pong answer from %s to %s", v.From, v.To)
|
log.Debugf("Got S2C pong answer from %s to %s", v.From, v.To)
|
||||||
|
|
||||||
j.ServerPingTimestampRx = time.Now().Unix() //nolint:wsl
|
j.ServerPingTimestampRx = time.Now().Unix() //nolint:wsl
|
||||||
|
|
||||||
// Похоже на понг второй стадии xep-0410 MUC-Ping-а, который у нас не реализован.
|
// Похоже на понг второй стадии xep-0410 MUC-Ping-а, который у нас не реализован.
|
||||||
@@ -378,13 +307,16 @@ func (j *Jabber) ParseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
|
|||||||
if mucNickMatch {
|
if mucNickMatch {
|
||||||
log.Debugf("Got 2-nd stage MUC pong answer (xep-0410) from %s to %s", v.From, v.To)
|
log.Debugf("Got 2-nd stage MUC pong answer (xep-0410) from %s to %s", v.From, v.To)
|
||||||
} else {
|
} else {
|
||||||
// Поскольку никакой логики у нас на этот счёт не предусмотрено, то просто пропускаем ответ
|
// Поскольку никакой логики у нас на этот счёт не предусмотрено, то просто пропускаем ответ.
|
||||||
log.Infof("Got unknown pong from %s to %s", v.From, v.To)
|
log.Infof("Got unknown pong from %s to %s", v.From, v.To)
|
||||||
log.Debug(spew.Sdump(e))
|
|
||||||
|
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
|
||||||
|
log.Debug(str)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Подтверждение бана
|
// Подтверждение бана.
|
||||||
case v.To == j.Talk.JID() && v.ID == "ban1":
|
case v.To == j.Talk.JID() && v.ID == "ban1":
|
||||||
mucNickMatch := false
|
mucNickMatch := false
|
||||||
|
|
||||||
@@ -402,27 +334,36 @@ func (j *Jabber) ParseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Формально, ответ должен парситься как пустой result и совпадать с типом jabberSimpleIqGetQuery
|
// Формально, ответ должен парситься как пустой result и совпадать с типом jabberSimpleIqGetQuery.
|
||||||
// Ответ приходит с 2 xmlns, похоже, он парсится неправильно в go-xmpp.
|
// Ответ приходит с 2 xmlns, похоже, он парсится неправильно в go-xmpp.
|
||||||
// TODO: после исправления go-xmpp, надо исправить это тут
|
// TODO: после исправления go-xmpp, надо исправить это тут.
|
||||||
var iqStruct SimpleIqGetQuery
|
var iqStruct SimpleIqGetQuery
|
||||||
|
|
||||||
if err := xml.Unmarshal(v.Query, &iqStruct); err == nil {
|
if err := xml.Unmarshal(v.Query, &iqStruct); err == nil {
|
||||||
if iqStruct.Xmlns == "http://jabber.org/protocol/muc#admin" && iqStruct.Text == "" {
|
if iqStruct.Xmlns == "http://jabber.org/protocol/muc#admin" && iqStruct.Text == "" {
|
||||||
if mucNickMatch {
|
if mucNickMatch {
|
||||||
// Поскольку никакой логики у нас на этот счёт не предусмотрено, то просто пропускаем ответ
|
// Поскольку никакой логики у нас на этот счёт не предусмотрено, то просто пропускаем ответ.
|
||||||
log.Infof("Got ban successful from %s to %s", v.From, v.To)
|
log.Infof("Got ban successful from %s to %s", v.From, v.To)
|
||||||
} else {
|
} else {
|
||||||
log.Infof("Got ban successful from %s to %s, from room we are not in", v.From, v.To)
|
log.Infof("Got ban successful from %s to %s, from room we are not in", v.From, v.To)
|
||||||
log.Debug(spew.Sdump(e))
|
|
||||||
|
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
|
||||||
|
log.Debug(str)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Infof("Got strange ban successful message from %s to %s", v.From, v.To)
|
log.Infof("Got strange ban successful message from %s to %s", v.From, v.To)
|
||||||
log.Debug(spew.Sdump(e))
|
|
||||||
|
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
|
||||||
|
log.Debug(str)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Infof("Got strange ban successful message from %s to %s", v.From, v.To)
|
log.Infof("Got strange ban successful message from %s to %s", v.From, v.To)
|
||||||
log.Debug(spew.Sdump(e))
|
|
||||||
|
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
|
||||||
|
log.Debug(str)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ответ с результатом адресован нам.
|
// Ответ с результатом адресован нам.
|
||||||
@@ -431,14 +372,14 @@ func (j *Jabber) ParseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
|
|||||||
if err := xml.Unmarshal(v.Query, &softwareVersion); err == nil {
|
if err := xml.Unmarshal(v.Query, &softwareVersion); err == nil {
|
||||||
if softwareVersion.Os == "" {
|
if softwareVersion.Os == "" {
|
||||||
log.Infof(
|
log.Infof(
|
||||||
"Recieved software version query result for %s: software=%s version=%s",
|
"Recieved software version query result for %s: name=%s version=%s",
|
||||||
v.From,
|
v.From,
|
||||||
softwareVersion.Name,
|
softwareVersion.Name,
|
||||||
softwareVersion.Version,
|
softwareVersion.Version,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
log.Infof(
|
log.Infof(
|
||||||
"Recieved software version query result for %s: software=%s version=%s os=%s",
|
"Recieved software version query result for %s: name=%s version=%s os=%s",
|
||||||
v.From,
|
v.From,
|
||||||
softwareVersion.Name,
|
softwareVersion.Name,
|
||||||
softwareVersion.Version,
|
softwareVersion.Version,
|
||||||
@@ -458,14 +399,20 @@ func (j *Jabber) ParseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.Info("Got an IQ result. Dunno how deal with it, discarding")
|
log.Info("Got an IQ result. Dunno how deal with it, discarding")
|
||||||
log.Debug(spew.Sdump(e))
|
|
||||||
|
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
|
||||||
|
log.Debug(str)
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
log.Info("Got an IQ result. Dunno how deal with it, discarding")
|
log.Info("Got an IQ result. Dunno how deal with it, discarding")
|
||||||
log.Debug(spew.Sdump(e))
|
|
||||||
|
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
|
||||||
|
log.Debug(str)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Этот бот не управляется со стороны сервера, поэтому все попытки порулить игнорируем
|
// Этот бот не управляется со стороны сервера, поэтому все попытки порулить игнорируем.
|
||||||
case xmpp.IQTypeSet:
|
case xmpp.IQTypeSet:
|
||||||
if muc, _ := strings.CutSuffix(v.To, "/"); muc != "" {
|
if muc, _ := strings.CutSuffix(v.To, "/"); muc != "" {
|
||||||
if slices.Contains(j.RoomsConnected, muc) {
|
if slices.Contains(j.RoomsConnected, muc) {
|
||||||
@@ -491,9 +438,10 @@ func (j *Jabber) ParseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug(spew.Sdump(e))
|
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
|
||||||
|
log.Debug(str)
|
||||||
// Нам прилетело сообщение об ошибке
|
}
|
||||||
|
// Нам прилетело сообщение об ошибке.
|
||||||
case xmpp.IQTypeError:
|
case xmpp.IQTypeError:
|
||||||
// Если сервер не хочет пинговаться и отвечает ошибкой на пинг, то наверно он не умеет в пинги,
|
// Если сервер не хочет пинговаться и отвечает ошибкой на пинг, то наверно он не умеет в пинги,
|
||||||
// хотя если мы его пингуем, значит он анонсировал такой capability. Вот, засранец!
|
// хотя если мы его пингуем, значит он анонсировал такой capability. Вот, засранец!
|
||||||
@@ -506,6 +454,7 @@ func (j *Jabber) ParseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
|
|||||||
if iqPingStruct.Xmlns == "urn:xmpp:ping" {
|
if iqPingStruct.Xmlns == "urn:xmpp:ping" {
|
||||||
if v.From == j.C.Jabber.Server {
|
if v.From == j.C.Jabber.Server {
|
||||||
msg := "Server announced that it can answer c2s ping, but gives us an error to such query, "
|
msg := "Server announced that it can answer c2s ping, but gives us an error to such query, "
|
||||||
|
|
||||||
msg += "fallback to keepalive whitespace pings"
|
msg += "fallback to keepalive whitespace pings"
|
||||||
log.Error(msg)
|
log.Error(msg)
|
||||||
|
|
||||||
@@ -515,13 +464,16 @@ func (j *Jabber) ParseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Error("Iq parsed as ping, but does not belong to xmlns urn:xmpp:ping")
|
log.Error("Iq parsed as ping, but does not belong to xmlns urn:xmpp:ping")
|
||||||
log.Error(spew.Sdump(e))
|
|
||||||
|
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
|
||||||
|
log.Error(str)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
parseError = false
|
parseError = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Это у нас пинг xep-0410 и мы не в комнате, предполагается, что надо бы заджойниться
|
// Это у нас пинг xep-0410 и мы не в комнате, предполагается, что надо бы заджойниться.
|
||||||
if parseError {
|
if parseError {
|
||||||
var iqErrorCancelNotAcceptable IqErrorCancelNotAcceptable
|
var iqErrorCancelNotAcceptable IqErrorCancelNotAcceptable
|
||||||
|
|
||||||
@@ -554,14 +506,68 @@ func (j *Jabber) ParseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
|
|||||||
"looks like message parsed as jabberIqErrorCancelNotAcceptable but we're not in given room",
|
"looks like message parsed as jabberIqErrorCancelNotAcceptable but we're not in given room",
|
||||||
)
|
)
|
||||||
|
|
||||||
log.Error(spew.Sdump(e))
|
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
|
||||||
|
log.Debug(str)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Error(
|
log.Error(
|
||||||
"Looks like message parsed as jabberIqErrorCancelNotAcceptable but addressed not to us",
|
"Looks like message parsed as jabberIqErrorCancelNotAcceptable but addressed not to us",
|
||||||
)
|
)
|
||||||
|
|
||||||
log.Error(spew.Sdump(e))
|
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
|
||||||
|
log.Debug(str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parseError = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if parseError {
|
||||||
|
var IqErrorCancelServiceNotAvailable IqErrorCancelServiceNotAvailable
|
||||||
|
|
||||||
|
if err := xml.Unmarshal(v.Query, &IqErrorCancelServiceNotAvailable); err == nil {
|
||||||
|
if v.To == j.Talk.JID() {
|
||||||
|
log.Infof("Jid %s disabled response to iq.version(?) request", v.From)
|
||||||
|
} else {
|
||||||
|
log.Error(
|
||||||
|
"Looks like message parsed as jabberIqErrorCancelServiceNotAvailable but addressed not to us",
|
||||||
|
)
|
||||||
|
|
||||||
|
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
|
||||||
|
log.Debug(str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parseError = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
<iq from='slackware-current@conference.jabber.ru/Batman46(new)' to='buny@jabber.ru/Лунная призма'
|
||||||
|
type='error' id='94e55122-6c15-4e88-a0df-ae9f02e0b550'>
|
||||||
|
<query xmlns='jabber:iq:version'/>
|
||||||
|
<error code='404' type='cancel'>
|
||||||
|
<item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
|
||||||
|
<text xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'>Адресата нет в конференции</text>
|
||||||
|
</error>
|
||||||
|
</iq>
|
||||||
|
*/
|
||||||
|
if parseError {
|
||||||
|
var IqErrorCancelItemNotFound IqErrorCancelItemNotFound
|
||||||
|
|
||||||
|
if err := xml.Unmarshal(v.Query, &IqErrorCancelItemNotFound); err == nil {
|
||||||
|
if v.To == j.Talk.JID() {
|
||||||
|
log.Infof("Jid %s disabled response to iq.version(?) request, hello batman46(new)", v.From)
|
||||||
|
} else {
|
||||||
|
log.Error(
|
||||||
|
"Looks like message parsed as jabberIqErrorCancelServiceNotAvailable but addressed not to us",
|
||||||
|
)
|
||||||
|
|
||||||
|
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
|
||||||
|
log.Debug(str)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
parseError = false
|
parseError = false
|
||||||
@@ -570,15 +576,22 @@ func (j *Jabber) ParseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
|
|||||||
|
|
||||||
if parseError {
|
if parseError {
|
||||||
log.Error("Unhandled IQ Error message")
|
log.Error("Unhandled IQ Error message")
|
||||||
log.Error(spew.Sdump(e))
|
|
||||||
|
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
|
||||||
|
log.Debug(str)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Нам прилетело что-то неизвестное из семейства IQ stanza
|
|
||||||
|
// Нам прилетело что-то неизвестное из семейства IQ stanza.
|
||||||
default:
|
default:
|
||||||
log.Info("Got an unknown IQ request. Dunno how deal with it, discarding")
|
log.Info("Got an unknown IQ request. Dunno how deal with it, discarding")
|
||||||
log.Info(spew.Sdump(e))
|
|
||||||
|
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
|
||||||
|
log.Debug(str)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Смена статуса участника
|
// Смена статуса участника.
|
||||||
case xmpp.Presence:
|
case xmpp.Presence:
|
||||||
if muc, _ := strings.CutSuffix(v.To, "/"); muc != "" {
|
if muc, _ := strings.CutSuffix(v.To, "/"); muc != "" {
|
||||||
if slices.Contains(j.RoomsConnected, muc) {
|
if slices.Contains(j.RoomsConnected, muc) {
|
||||||
@@ -593,25 +606,48 @@ func (j *Jabber) ParseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
|
|||||||
// клиента забанят из-за спама "вошёл-вышел".
|
// клиента забанят из-за спама "вошёл-вышел".
|
||||||
// Однако, это не значит, что такую ситуацию мы не должны корректным образом обрабатывать.
|
// Однако, это не значит, что такую ситуацию мы не должны корректным образом обрабатывать.
|
||||||
if v.Type == "unavailable" {
|
if v.Type == "unavailable" {
|
||||||
// Считаем, что мы больше не в комнате, поэтому не знаем, кто там есть
|
// Считаем, что мы больше не в комнате, поэтому не знаем, кто там есть.
|
||||||
j.RoomPresences.Delete(v.From)
|
j.RoomPresences.Delete(v.From)
|
||||||
|
|
||||||
log.Error("Presence notification - looks like another instance of client leaves room")
|
log.Error("Presence notification - looks like another instance of client leaves room")
|
||||||
|
|
||||||
if slices.Contains(j.RoomsConnected, v.From) {
|
if slices.Contains(j.RoomsConnected, v.From) {
|
||||||
go j.JoinMuc(v.From)
|
j.GTomb.Go(
|
||||||
|
func() error {
|
||||||
|
if err := j.JoinMuc(v.From); err != nil {
|
||||||
|
return fmt.Errorf("unable to join MUC %s: %w", v.From, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Errorf("Presence notification, Type: %s, From: %s, Show: %s, Status: %s",
|
log.Errorf("Presence notification, Type: %s, From: %s, Show: %s, Status: %s",
|
||||||
v.Type, v.From, v.Show, v.Status)
|
v.Type, v.From, v.Show, v.Status)
|
||||||
log.Error(spew.Sdump(v))
|
|
||||||
|
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
|
||||||
|
log.Error(str)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Infof(
|
log.Infof(
|
||||||
"Presence notification, Type: %s, From: %s, To: %s Show: %s, Status: %s, Affiliation: %s, Role: %s, JID: %s",
|
"Presence notification, Type: %s, From: %s, To: %s Show: %s, Status: %s, Affiliation: %s, Role: %s, JID: %s, Priority: %s",
|
||||||
v.Type, v.From, v.To, v.Show, v.Status, v.Affiliation, v.Role, v.JID,
|
v.Type, v.From, v.To, v.Show, v.Status, v.Affiliation, v.Role, v.JID, v.Priority,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if !strings.Contains(v.From, "/") {
|
||||||
|
log.Debugf("Looks like we have got Presence notification from roster: %s, skip it", v.From)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if v.JID == "" {
|
||||||
|
log.Info("Useless presence notification without JID field, skipping")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
room := strings.SplitN(v.From, "/", 2)[0]
|
room := strings.SplitN(v.From, "/", 2)[0]
|
||||||
nick := strings.SplitN(v.From, "/", 2)[1]
|
nick := strings.SplitN(v.From, "/", 2)[1]
|
||||||
|
|
||||||
@@ -621,65 +657,167 @@ func (j *Jabber) ParseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Это наш собственный Presence
|
// На всякий случай добавим комнату, из которой пришло уведомление в список тех, в которые вхож бот.
|
||||||
if v.Show == "" && v.Status == "" {
|
if nick == j.GetBotNickFromRoomConfig(room) {
|
||||||
if nick == j.GetBotNickFromRoomConfig(room) {
|
j.RoomsConnected = append(j.RoomsConnected, room)
|
||||||
j.RoomsConnected = append(j.RoomsConnected, room)
|
// На всякий случай дедуплицируем список комнат, к которым мы заджойнились.
|
||||||
// На всякий случай дедуплицируем список комнат, к которым мы заджойнились.
|
sort.Strings(j.RoomsConnected)
|
||||||
sort.Strings(j.RoomsConnected)
|
|
||||||
slices.Compact(j.RoomsConnected)
|
j.RoomsConnected = slices.Compact(j.RoomsConnected)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch v.Role {
|
switch v.Role {
|
||||||
// Участник ушёл
|
// Участник ушёл.
|
||||||
case "none":
|
case "none":
|
||||||
|
var (
|
||||||
|
newPresences = make([]xmpp.Presence, 0)
|
||||||
|
presenceJSON []byte
|
||||||
|
ok bool
|
||||||
|
presences []xmpp.Presence
|
||||||
|
)
|
||||||
|
|
||||||
|
// Обновлять базу имеет смысл только если есть хоть какая-то запись о комнате в ней.
|
||||||
if presenceJSONInterface, present := j.RoomPresences.Get(room); present {
|
if presenceJSONInterface, present := j.RoomPresences.Get(room); present {
|
||||||
presenceJSONStrings := InterfaceToStringSlice(presenceJSONInterface)
|
presenceJSON, ok = presenceJSONInterface.([]byte)
|
||||||
|
|
||||||
var newPresenceJSONStrings []string
|
if !ok {
|
||||||
|
log.Errorf("Looks like we have corrupted entry in presence db")
|
||||||
|
|
||||||
for _, presenceJSONstring := range presenceJSONStrings {
|
return
|
||||||
var p xmpp.Presence
|
}
|
||||||
_ = json.Unmarshal([]byte(presenceJSONstring), &p)
|
|
||||||
|
|
||||||
|
if err := json.Unmarshal(presenceJSON, &presences); err != nil {
|
||||||
|
log.Errorf("Looks like we have corrupted entry in presence db")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range presences { //nolint: gocritic // Развели тут, блять, страну советов, напиши что надо сделать кодом, сука.
|
||||||
|
// Если находим, что у нас уже есть клиент с таким же From, то есть полным nick-ом (для groupchat)
|
||||||
|
// пропускаем его.
|
||||||
if p.From == v.From {
|
if p.From == v.From {
|
||||||
|
if j.C.Debug.PresenceDB {
|
||||||
|
log.Debugf("Skipping presence for %s in room %s", v.JID, room)
|
||||||
|
}
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
newPresenceJSONStrings = append(newPresenceJSONStrings, presenceJSONstring)
|
if j.C.Debug.PresenceDB {
|
||||||
|
log.Debugf("Appending presence for %s in room %s", v.JID, room)
|
||||||
|
}
|
||||||
|
|
||||||
|
newPresences = append(newPresences, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
j.RoomPresences.Set(room, newPresenceJSONStrings)
|
presenceJSON, _ = json.Marshal(newPresences) //nolint: errchkjson
|
||||||
|
|
||||||
|
if j.C.Debug.PresenceDB {
|
||||||
|
log.Debugf("Updating presence db for room %s", room)
|
||||||
|
}
|
||||||
|
|
||||||
|
j.RoomPresences.Set(room, presenceJSON)
|
||||||
|
|
||||||
|
// Больше нам, похоже с этим уведомлением нечего.
|
||||||
|
return
|
||||||
}
|
}
|
||||||
// Участник пришёл
|
|
||||||
|
// Участник пришёл или изменил свой статус.
|
||||||
default:
|
default:
|
||||||
var (
|
var (
|
||||||
presenceJSONStrings []string
|
newPresences = make([]xmpp.Presence, 0)
|
||||||
newPresenceJSONStrings []string
|
presenceJSON []byte
|
||||||
presenceJSONBytes []byte
|
ok bool
|
||||||
|
presences []xmpp.Presence
|
||||||
|
skip bool
|
||||||
)
|
)
|
||||||
|
|
||||||
if presenceJSONInterface, present := j.RoomPresences.Get(room); present {
|
if presenceJSONInterface, present := j.RoomPresences.Get(room); present {
|
||||||
presenceJSONStrings = InterfaceToStringSlice(presenceJSONInterface)
|
presenceJSON, ok = presenceJSONInterface.([]byte)
|
||||||
}
|
if !ok {
|
||||||
|
log.Errorf("Looks like we have corrupted entry in presence db")
|
||||||
|
|
||||||
for _, presenceJSONString := range presenceJSONStrings {
|
return
|
||||||
var p xmpp.Presence
|
|
||||||
_ = json.Unmarshal([]byte(presenceJSONString), &p)
|
|
||||||
|
|
||||||
// Если находим, что у нас уже есть клиент с таким же From, то есть полным nick-ом (для groupchat)
|
|
||||||
// просто замещаем его.
|
|
||||||
if p.From == v.From {
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
newPresenceJSONStrings = append(newPresenceJSONStrings, presenceJSONString)
|
// Если мы нашли запись о комнате, то её надо обновить.
|
||||||
|
if err := json.Unmarshal(presenceJSON, &presences); err != nil {
|
||||||
|
log.Errorf("Looks like we have corrupted entry in presence db")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range presences { //nolint: gocritic // Развели тут, блять, страну советов, напиши что надо сделать кодом, сука.
|
||||||
|
// Если находим, что у нас уже есть клиент с таким же From, то есть полным nick-ом (для groupchat)
|
||||||
|
// запоминаем его и добавим позднее.
|
||||||
|
if p.From == v.From {
|
||||||
|
// Запоминаем участника только если его роль не поменялась на outcast. (То есть если это не
|
||||||
|
// бан.)
|
||||||
|
if v.Role == "outcast" {
|
||||||
|
if j.C.Debug.PresenceDB {
|
||||||
|
log.Debugf("Let's forget about %s because it is outcast/banned", p.JID)
|
||||||
|
}
|
||||||
|
|
||||||
|
skip = true
|
||||||
|
} else if j.C.Debug.PresenceDB {
|
||||||
|
log.Debugf("Skip adding %s to presences for %s, add as last one", p.JID, room)
|
||||||
|
}
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if j.C.Debug.PresenceDB {
|
||||||
|
log.Debugf("Appending presence for %s in room %s", v.JID, room)
|
||||||
|
}
|
||||||
|
|
||||||
|
newPresences = append(newPresences, p)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
presenceJSONBytes, _ = json.Marshal(v) //nolint:errchkjson
|
if !skip {
|
||||||
newPresenceJSONStrings = append(newPresenceJSONStrings, string(presenceJSONBytes))
|
if j.C.Debug.PresenceDB {
|
||||||
j.RoomPresences.Set(room, newPresenceJSONStrings)
|
log.Debugf("Appending presence for %s in room %s", v.JID, room)
|
||||||
|
}
|
||||||
|
|
||||||
|
newPresences = append(newPresences, v)
|
||||||
|
} else if j.C.Debug.PresenceDB {
|
||||||
|
log.Debugf("Skip Appending outcast presence for %s in room %s", v.JID, room)
|
||||||
|
}
|
||||||
|
|
||||||
|
presenceJSON, _ = json.Marshal(newPresences) //nolint: errchkjson
|
||||||
|
|
||||||
|
if j.C.Debug.PresenceDB {
|
||||||
|
log.Debugf("Updating presence db for room %s", room)
|
||||||
|
}
|
||||||
|
|
||||||
|
j.RoomPresences.Set(room, presenceJSON)
|
||||||
|
}
|
||||||
|
|
||||||
|
if j.C.Debug.PresenceDB {
|
||||||
|
log.Debugf("List of presence entries in presence db of %s:", room)
|
||||||
|
|
||||||
|
if presenceJSONInterface, present := j.RoomPresences.Get(room); present {
|
||||||
|
var presences []xmpp.Presence
|
||||||
|
|
||||||
|
presenceJSON, ok := presenceJSONInterface.([]byte)
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
log.Debugf("Looks like we have corrupted entry in presence db")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Если мы нашли запись о комнате, то её надо обновить.
|
||||||
|
if err := json.Unmarshal(presenceJSON, &presences); err != nil {
|
||||||
|
log.Debugf("Looks like we have corrupted entry in presence db")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range presences { //nolint: gocritic // Развели тут, блять, страну советов, напиши что надо сделать кодом, сука.
|
||||||
|
log.Debugf("Found presence for %s from %s", p.JID, room)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Проверяем, а не злодей ли зашёл? Сделать это мы можем, только если мы находимся в комнате.
|
// Проверяем, а не злодей ли зашёл? Сделать это мы можем, только если мы находимся в комнате.
|
||||||
@@ -696,7 +834,7 @@ func (j *Jabber) ParseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ответ на запрос поддерживаемых фич, который "http://jabber.org/protocol/disco#info"
|
// Ответ на запрос поддерживаемых фич, который "http://jabber.org/protocol/disco#info".
|
||||||
case xmpp.DiscoResult:
|
case xmpp.DiscoResult:
|
||||||
if muc, _ := strings.CutSuffix(v.To, "/"); muc != "" {
|
if muc, _ := strings.CutSuffix(v.To, "/"); muc != "" {
|
||||||
if slices.Contains(j.RoomsConnected, muc) {
|
if slices.Contains(j.RoomsConnected, muc) {
|
||||||
@@ -704,12 +842,12 @@ func (j *Jabber) ParseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Я видел 2 типа disco result и они отличались только []identities. Попробуем вытащить известный identity
|
// Я видел 2 типа disco result и они отличались только []identities. Попробуем вытащить известный identity.
|
||||||
// TODO: проверять адресата v.To
|
// TODO: проверять адресата v.To.
|
||||||
for _, ident := range v.Identities {
|
for _, ident := range v.Identities {
|
||||||
switch ident.Category {
|
switch ident.Category {
|
||||||
case "server":
|
case "server":
|
||||||
// Конкретно сейчас нас интересует только поддержка c2s ping
|
// Конкретно сейчас нас интересует только поддержка c2s ping.
|
||||||
for _, feature := range v.Features {
|
for _, feature := range v.Features {
|
||||||
log.Debugf("Server %s announced that it supports feature: %s", v.From, feature)
|
log.Debugf("Server %s announced that it supports feature: %s", v.From, feature)
|
||||||
j.ServerCapsList.Set(feature, true)
|
j.ServerCapsList.Set(feature, true)
|
||||||
@@ -722,6 +860,7 @@ func (j *Jabber) ParseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
|
|||||||
|
|
||||||
for _, feature := range v.Features {
|
for _, feature := range v.Features {
|
||||||
log.Debugf("MUC %s announced that it supports feature: %s", v.From, feature)
|
log.Debugf("MUC %s announced that it supports feature: %s", v.From, feature)
|
||||||
|
|
||||||
mucCaps[feature] = true //nolint:wsl
|
mucCaps[feature] = true //nolint:wsl
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -732,13 +871,18 @@ func (j *Jabber) ParseEvent(e interface{}) { //nolint:maintidx,gocognit,gocyclo
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
log.Debug("Got unknown reply to disco#info")
|
log.Debug("Got unknown reply to disco#info")
|
||||||
log.Debug(spew.Sdump(e))
|
|
||||||
|
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
|
||||||
|
log.Debug(str)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Это что-то неизвестное, подампим событие в лог
|
// Это что-то неизвестное, подампим событие в лог.
|
||||||
default:
|
default:
|
||||||
log.Info(spew.Sdump(e))
|
for str := range strings.SplitSeq(spew.Sdump(e), "\n") {
|
||||||
|
log.Info(str)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
package jabber
|
package jabber
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"buny-jabber-bot/internal/log"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// MyLoop - основной цикл программы.
|
// MyLoop - основной цикл программы.
|
||||||
func (j *Jabber) MyLoop() error {
|
func (j *Jabber) MyLoop() error {
|
||||||
for {
|
for {
|
||||||
// Зададим начальное значение глобальным переменным
|
// Зададим начальное значение глобальным переменным.
|
||||||
j.ServerPingTimestampRx = 0
|
j.ServerPingTimestampRx = 0
|
||||||
j.ServerPingTimestampTx = 0
|
j.ServerPingTimestampTx = 0
|
||||||
j.RoomsConnected = make([]string, 1)
|
j.RoomsConnected = make([]string, 1)
|
||||||
@@ -23,9 +22,9 @@ func (j *Jabber) MyLoop() error {
|
|||||||
j.ServerPingTimestampRx = 0
|
j.ServerPingTimestampRx = 0
|
||||||
j.RoomPresences = NewCollection()
|
j.RoomPresences = NewCollection()
|
||||||
|
|
||||||
// Установим коннект
|
// Установим коннект.
|
||||||
if err := j.EstablishConnection(); err != nil {
|
if err := j.EstablishConnection(); err != nil {
|
||||||
log.Error(err)
|
log.Error(err.Error())
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -36,7 +35,7 @@ func (j *Jabber) MyLoop() error {
|
|||||||
j.GTomb.Go(
|
j.GTomb.Go(
|
||||||
func() error {
|
func() error {
|
||||||
err := j.ProbeServerLiveness()
|
err := j.ProbeServerLiveness()
|
||||||
log.Error(err)
|
log.Error(err.Error())
|
||||||
|
|
||||||
return err
|
return err
|
||||||
},
|
},
|
||||||
@@ -48,14 +47,8 @@ func (j *Jabber) MyLoop() error {
|
|||||||
// j.GTomb.Go() тут зовётся внутри на каждую комнату, если у нас ошибка, эта горутинка рано или поздно выйдет.
|
// j.GTomb.Go() тут зовётся внутри на каждую комнату, если у нас ошибка, эта горутинка рано или поздно выйдет.
|
||||||
go j.ProbeMUCLiveness()
|
go j.ProbeMUCLiveness()
|
||||||
|
|
||||||
// Гребём ивенты...
|
// Гребём ивенты, пока стриггерилось завершение работы приложения, или соединение не порвалось.
|
||||||
for {
|
for !j.Shutdown {
|
||||||
// Стриггерилось завершение работы приложения, или соединение не установлено (порвалось, например)
|
|
||||||
// грести не надо
|
|
||||||
if j.Shutdown {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if !j.IsConnected {
|
if !j.IsConnected {
|
||||||
// Tight loop - это наверно не очень хорошо, думаю, ничего страшного не будет, если мы поспим 100мс.
|
// Tight loop - это наверно не очень хорошо, думаю, ничего страшного не будет, если мы поспим 100мс.
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
@@ -63,7 +56,7 @@ func (j *Jabber) MyLoop() error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Вынимаем ивент из "провода"
|
// Вынимаем ивент из "провода".
|
||||||
chat, err := j.Talk.Recv()
|
chat, err := j.Talk.Recv()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
package jabber
|
package jabber
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"buny-jabber-bot/internal/log"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/eleksir/go-xmpp"
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
log "github.com/sirupsen/logrus"
|
"github.com/xmppo/go-xmpp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// QuerySoftwareVersion запрошивает версию и название клиента.
|
// QuerySoftwareVersion запрашивает версию и название клиента.
|
||||||
func (j *Jabber) QuerySoftwareVersion(jid string) (string, error) {
|
func (j *Jabber) QuerySoftwareVersion(jid string) (string, error) {
|
||||||
var (
|
var (
|
||||||
id string
|
id string
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package jabber
|
package jabber
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"buny-jabber-bot/internal/log"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -8,7 +9,6 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/hjson/hjson-go"
|
"github.com/hjson/hjson-go"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ReadConfig читает и валидирует конфиг, а также выставляет некоторые default-ы, если значений для параметров в конфиге
|
// ReadConfig читает и валидирует конфиг, а также выставляет некоторые default-ы, если значений для параметров в конфиге
|
||||||
@@ -28,7 +28,7 @@ func (j *Jabber) ReadConfig() error { //nolint:gocognit,gocyclo
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
configJSONPath := fmt.Sprintf("%s/data/config.json", filepath.Dir(executablePath))
|
configJSONPath := filepath.Dir(executablePath) + "/data/config.json"
|
||||||
|
|
||||||
locations := []string{
|
locations := []string{
|
||||||
"~/.buny-jabber-bot.json",
|
"~/.buny-jabber-bot.json",
|
||||||
@@ -66,7 +66,7 @@ func (j *Jabber) ReadConfig() error { //nolint:gocognit,gocyclo
|
|||||||
// сериализуем в json, а потом json превращаем в структурку. Не очень эффективно, но он и нечасто требуется.
|
// сериализуем в json, а потом json превращаем в структурку. Не очень эффективно, но он и нечасто требуется.
|
||||||
var (
|
var (
|
||||||
sampleConfig MyConfig
|
sampleConfig MyConfig
|
||||||
tmp map[string]interface{}
|
tmp map[string]any
|
||||||
)
|
)
|
||||||
|
|
||||||
err = hjson.Unmarshal(buf, &tmp)
|
err = hjson.Unmarshal(buf, &tmp)
|
||||||
@@ -80,7 +80,7 @@ func (j *Jabber) ReadConfig() error { //nolint:gocognit,gocyclo
|
|||||||
|
|
||||||
tmpJSON, err := json.Marshal(tmp)
|
tmpJSON, err := json.Marshal(tmp)
|
||||||
|
|
||||||
// Не удалось преобразовать map-ку в json
|
// Не удалось преобразовать map-ку в json.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("Skip parsing config file %s: %s", location, err)
|
log.Warnf("Skip parsing config file %s: %s", location, err)
|
||||||
|
|
||||||
@@ -93,9 +93,10 @@ func (j *Jabber) ReadConfig() error { //nolint:gocognit,gocyclo
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Значения для Jabber-клиента
|
// Значения для Jabber-клиента.
|
||||||
if sampleConfig.Jabber.Server == "" {
|
if sampleConfig.Jabber.Server == "" {
|
||||||
log.Error("Jabber server is not defined in config, using localhost")
|
log.Error("Jabber server is not defined in config, using localhost")
|
||||||
|
|
||||||
sampleConfig.Jabber.Server = "localhost" //nolint:wsl
|
sampleConfig.Jabber.Server = "localhost" //nolint:wsl
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,7 +122,7 @@ func (j *Jabber) ReadConfig() error { //nolint:gocognit,gocyclo
|
|||||||
sampleConfig.Jabber.SslVerify = false
|
sampleConfig.Jabber.SslVerify = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// sampleConfig.Jabber.InsecureAllowUnencryptedAuth = false, если не задан
|
// sampleConfig.Jabber.InsecureAllowUnencryptedAuth = false, если не задан.
|
||||||
|
|
||||||
if sampleConfig.Jabber.ConnectionTimeout == 0 {
|
if sampleConfig.Jabber.ConnectionTimeout == 0 {
|
||||||
sampleConfig.Jabber.ConnectionTimeout = 10
|
sampleConfig.Jabber.ConnectionTimeout = 10
|
||||||
@@ -175,14 +176,14 @@ func (j *Jabber) ReadConfig() error { //nolint:gocognit,gocyclo
|
|||||||
log.Infof("Jabber user not defined in config, guessing, it can be %s", sampleConfig.Jabber.User)
|
log.Infof("Jabber user not defined in config, guessing, it can be %s", sampleConfig.Jabber.User)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Если sampleConfig.Jabber.Password не задан, то авторизации не будет
|
// Если sampleConfig.Jabber.Password не задан, то авторизации не будет.
|
||||||
|
|
||||||
// Если не задано ни одного мастера, то бот сам себе мастер
|
// Если не задано ни одного мастера, то бот сам себе мастер.
|
||||||
if len(sampleConfig.Jabber.BotMasters) == 0 {
|
if len(sampleConfig.Jabber.BotMasters) == 0 {
|
||||||
sampleConfig.Jabber.BotMasters[0] = sampleConfig.Jabber.User
|
sampleConfig.Jabber.BotMasters[0] = sampleConfig.Jabber.User
|
||||||
}
|
}
|
||||||
|
|
||||||
// Нам бот нужен в каких-то чат-румах, а не "просто так"
|
// Нам бот нужен в каких-то чат-румах, а не "просто так".
|
||||||
if len(sampleConfig.Jabber.Channels) < 1 {
|
if len(sampleConfig.Jabber.Channels) < 1 {
|
||||||
return errors.New("no jabber channels/rooms defined in config, quitting") //nolint:goerr113
|
return errors.New("no jabber channels/rooms defined in config, quitting") //nolint:goerr113
|
||||||
}
|
}
|
||||||
@@ -196,8 +197,8 @@ func (j *Jabber) ReadConfig() error { //nolint:gocognit,gocyclo
|
|||||||
sampleConfig.Jabber.Channels[n].Nick = sampleConfig.Jabber.Nick
|
sampleConfig.Jabber.Channels[n].Nick = sampleConfig.Jabber.Nick
|
||||||
}
|
}
|
||||||
|
|
||||||
// channel.Password может быть пустым, тогда пароля нет
|
// channel.Password может быть пустым, тогда пароля нет.
|
||||||
// channel.Bayes.Enabled будет false, если не проставлен
|
// channel.Bayes.Enabled будет false, если не проставлен.
|
||||||
|
|
||||||
if channel.Bayes.MinLength < 40 {
|
if channel.Bayes.MinLength < 40 {
|
||||||
channel.Bayes.MinLength = 40
|
channel.Bayes.MinLength = 40
|
||||||
@@ -215,7 +216,7 @@ func (j *Jabber) ReadConfig() error { //nolint:gocognit,gocyclo
|
|||||||
channel.Bayes.DefaultAction = "log"
|
channel.Bayes.DefaultAction = "log"
|
||||||
}
|
}
|
||||||
|
|
||||||
// channel.AllCaps.Enabled будет false, если не указан
|
// channel.AllCaps.Enabled будет false, если не указан.
|
||||||
if channel.AllCaps.MinLength < 10 {
|
if channel.AllCaps.MinLength < 10 {
|
||||||
channel.AllCaps.MinLength = 10
|
channel.AllCaps.MinLength = 10
|
||||||
}
|
}
|
||||||
@@ -229,23 +230,23 @@ func (j *Jabber) ReadConfig() error { //nolint:gocognit,gocyclo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Если список фраз с которыми стартует бот пустой, вносим в него 1 запись с пустой строкой
|
// Если список фраз с которыми стартует бот пустой, вносим в него 1 запись с пустой строкой.
|
||||||
if len(sampleConfig.Jabber.StartupStatus) == 0 {
|
if len(sampleConfig.Jabber.StartupStatus) == 0 {
|
||||||
sampleConfig.Jabber.StartupStatus[0] = ""
|
sampleConfig.Jabber.StartupStatus[0] = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// Если список статусов, с которыми работает бот пустой, вносим в него 1 запись с пустой строкой
|
// Если список статусов, с которыми работает бот пустой, вносим в него 1 запись с пустой строкой.
|
||||||
if len(sampleConfig.Jabber.RuntimeStatus.Text) == 0 {
|
if len(sampleConfig.Jabber.RuntimeStatus.Text) == 0 {
|
||||||
sampleConfig.Jabber.RuntimeStatus.Text[0] = ""
|
sampleConfig.Jabber.RuntimeStatus.Text[0] = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// Если sampleConfig.Jabber.RuntimeStatus.RotationTime не задан, то он равен 0
|
// Если sampleConfig.Jabber.RuntimeStatus.RotationTime не задан, то он равен 0.
|
||||||
// Если sampleConfig.Jabber.RuntimeStatus.RotationSplayTime не задан, то он равен 0
|
// Если sampleConfig.Jabber.RuntimeStatus.RotationSplayTime не задан, то он равен 0.
|
||||||
|
|
||||||
// Если sampleConfig.Jabber.BanDelay не задан, то он равен 0
|
// Если sampleConfig.Jabber.BanDelay не задан, то он равен 0.
|
||||||
// Если sampleConfig.Jabber.BanPhrasesEnable не задан, то он false
|
// Если sampleConfig.Jabber.BanPhrasesEnable не задан, то он false.
|
||||||
|
|
||||||
// Если список фраз, с которыми банят пустой, то вносим в него одну позицию с пустой строкой
|
// Если список фраз, с которыми банят пустой, то вносим в него одну позицию с пустой строкой.
|
||||||
if len(sampleConfig.Jabber.BanPhrases) == 0 {
|
if len(sampleConfig.Jabber.BanPhrases) == 0 {
|
||||||
sampleConfig.Jabber.BanPhrases[0] = ""
|
sampleConfig.Jabber.BanPhrases[0] = ""
|
||||||
}
|
}
|
||||||
@@ -260,7 +261,8 @@ func (j *Jabber) ReadConfig() error { //nolint:gocognit,gocyclo
|
|||||||
log.Info("loglevel not defined in config, using info")
|
log.Info("loglevel not defined in config, using info")
|
||||||
}
|
}
|
||||||
|
|
||||||
// sampleConfig.Log = "" if not set
|
// sampleConfig.DebugLib = false
|
||||||
|
// sampleConfig.Log = "" if not set.
|
||||||
|
|
||||||
j.C = sampleConfig
|
j.C = sampleConfig
|
||||||
configLoaded = true
|
configLoaded = true
|
||||||
|
|||||||
@@ -1,24 +1,26 @@
|
|||||||
package jabber
|
package jabber
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"buny-jabber-bot/internal/log"
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/eleksir/go-xmpp"
|
"github.com/xmppo/go-xmpp"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// SigHandler срабатывает если происходит завершение программы, инициированное либо init-ом, либо пользователем.
|
||||||
|
// Обрабатвает сигналы INT, TERM, QUIT.
|
||||||
func (j *Jabber) SigHandler() error {
|
func (j *Jabber) SigHandler() error {
|
||||||
log.Debug("Installing signal handler")
|
log.Debug("Installing signal handler")
|
||||||
|
|
||||||
for s := range j.SigChan {
|
for s := range j.SigChan {
|
||||||
switch s {
|
switch s {
|
||||||
case syscall.SIGINT:
|
case syscall.SIGINT:
|
||||||
log.Infoln("Got SIGINT, quitting")
|
log.Info("Got SIGINT, quitting")
|
||||||
case syscall.SIGTERM:
|
case syscall.SIGTERM:
|
||||||
log.Infoln("Got SIGTERM, quitting")
|
log.Info("Got SIGTERM, quitting")
|
||||||
case syscall.SIGQUIT:
|
case syscall.SIGQUIT:
|
||||||
log.Infoln("Got SIGQUIT, quitting")
|
log.Info("Got SIGQUIT, quitting")
|
||||||
|
|
||||||
// Заходим на новую итерацию, если у нас "неинтересный" сигнал.
|
// Заходим на новую итерацию, если у нас "неинтересный" сигнал.
|
||||||
default:
|
default:
|
||||||
@@ -46,7 +48,7 @@ func (j *Jabber) SigHandler() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// И закрываем соединение.
|
// И закрываем соединение.
|
||||||
log.Infoln("Closing connection to jabber server")
|
log.Info("Closing connection to jabber server")
|
||||||
|
|
||||||
if err := j.Talk.Close(); err != nil {
|
if err := j.Talk.Close(); err != nil {
|
||||||
log.Infof("Unable to close connection to jabber server: %s", err)
|
log.Infof("Unable to close connection to jabber server: %s", err)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/eleksir/go-xmpp"
|
"github.com/xmppo/go-xmpp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Squash банит указанный jid в указанной комнате.
|
// Squash банит указанный jid в указанной комнате.
|
||||||
@@ -38,6 +38,7 @@ func (j *Jabber) Squash(room, jid string, reasonEnable bool, vType string) (stri
|
|||||||
|
|
||||||
if reasonEnable {
|
if reasonEnable {
|
||||||
var t = time.Now()
|
var t = time.Now()
|
||||||
|
|
||||||
ban += fmt.Sprintf(
|
ban += fmt.Sprintf(
|
||||||
"<reason>autoban at %04d.%02d.%02d %02d:%02d:%02d</reason>",
|
"<reason>autoban at %04d.%02d.%02d %02d:%02d:%02d</reason>",
|
||||||
t.Year(),
|
t.Year(),
|
||||||
|
|||||||
@@ -4,212 +4,264 @@ import (
|
|||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/eleksir/go-xmpp"
|
"github.com/xmppo/go-xmpp"
|
||||||
"gopkg.in/tomb.v2"
|
"gopkg.in/tomb.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MyConfig прототип структурки с конфигом.
|
type (
|
||||||
type MyConfig struct {
|
// MyConfig прототип структурки с конфигом.
|
||||||
|
MyConfig struct {
|
||||||
|
CSign string `json:"csign,omitempty"`
|
||||||
|
Loglevel string `json:"loglevel,omitempty"`
|
||||||
|
Log string `json:"log,omitempty"`
|
||||||
|
Debug struct {
|
||||||
|
Lib bool `json:"lib,omitempty"`
|
||||||
|
Ping bool `json:"ping,omitempty"`
|
||||||
|
PresenceDB bool `json:"presence_db,omitempty"`
|
||||||
|
} `json:"debug"`
|
||||||
|
Version string `json:"version,omitempty"`
|
||||||
|
ExeName string `json:"exe_name,omitempty"`
|
||||||
|
Jabber struct {
|
||||||
|
Server string `json:"server,omitempty"`
|
||||||
|
Nick string `json:"nick,omitempty"`
|
||||||
|
Resource string `json:"resource,omitempty"`
|
||||||
|
User string `json:"user,omitempty"`
|
||||||
|
Password string `json:"password,omitempty"`
|
||||||
|
StartupStatus []string `json:"startup_status,omitempty"`
|
||||||
|
BotMasters []string `json:"bot_masters,omitempty"`
|
||||||
|
BanPhrases []string `json:"ban_phrases,omitempty"`
|
||||||
|
RuntimeStatus struct {
|
||||||
|
Text []string `json:"text,omitempty"`
|
||||||
|
RotationTime int64 `json:"rotation_time,omitempty"`
|
||||||
|
RotationSplayTime int64 `json:"rotation_splay_time,omitempty"`
|
||||||
|
} `json:"runtime_status"`
|
||||||
|
Channels []struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Nick string `json:"nick,omitempty"`
|
||||||
|
Password string `json:"password,omitempty"`
|
||||||
|
AllCaps struct {
|
||||||
|
DefaultAction string `json:"default_action,omitempty"`
|
||||||
|
MinLength int `json:"min_length,omitempty"`
|
||||||
|
Enabled bool `json:"enabled,omitempty"`
|
||||||
|
} `json:"all_caps"`
|
||||||
|
Bayes struct {
|
||||||
|
DefaultAction string `json:"default_action,omitempty"`
|
||||||
|
MinWords int64 `json:"min_words,omitempty"`
|
||||||
|
MinLength int `json:"min_length,omitempty"`
|
||||||
|
Enabled bool `json:"enabled,omitempty"`
|
||||||
|
} `json:"bayes"`
|
||||||
|
} `json:"channels"`
|
||||||
|
ConnectionTimeout int64 `json:"connection_timeout,omitempty"`
|
||||||
|
ReconnectDelay int64 `json:"reconnect_delay,omitempty"`
|
||||||
|
ServerPingDelay int64 `json:"server_ping_delay,omitempty"`
|
||||||
|
MucPingDelay int64 `json:"muc_ping_delay,omitempty"`
|
||||||
|
MucRejoinDelay int64 `json:"muc_rejoin_delay,omitempty"`
|
||||||
|
PingSplayDelay int64 `json:"ping_splay_delay,omitempty"`
|
||||||
|
BanDelay int64 `json:"ban_delay,omitempty"`
|
||||||
|
Port int `json:"port,omitempty"`
|
||||||
|
Ssl bool `json:"ssl,omitempty"`
|
||||||
|
StartTLS bool `json:"starttls,omitempty"`
|
||||||
|
SslVerify bool `json:"ssl_verify,omitempty"`
|
||||||
|
InsecureAllowUnencryptedAuth bool `json:"insecureallowunencryptedauth,omitempty"`
|
||||||
|
BanPhrasesEnable bool `json:"ban_phrases_enable,omitempty"`
|
||||||
|
} `json:"jabber"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MyWhiteList прототип структурки с белым списком jid-ов.
|
||||||
|
MyWhiteList struct {
|
||||||
|
Whitelist []struct {
|
||||||
|
RoomName string `json:"room_name"`
|
||||||
|
Jid []string `json:"jid,omitempty"`
|
||||||
|
WipeBans bool `json:"wipe_bans,omitempty"`
|
||||||
|
} `json:"whitelist,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MyBlackList прототип структурки с чёрным списком jid-ов.
|
||||||
|
MyBlackList struct {
|
||||||
|
Blacklist []MyBlackListEntry `json:"blacklist"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MyBlackListEntry вложенная структура для MyBlackList.
|
||||||
|
MyBlackListEntry struct {
|
||||||
|
RoomName string `json:"room_name"`
|
||||||
|
JidRe []string `json:"jid_re"`
|
||||||
|
NickRe []string `json:"nick_re"`
|
||||||
|
PhraseRe []string `json:"phrase_re"`
|
||||||
|
UserAgent []MyBlackListEntryUserAgent `json:"user_agent"`
|
||||||
|
ReasonEnable bool `json:"reason_enable,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MyBlackListEntryUserAgent вложенная структура для MyBlackListEntry.
|
||||||
|
MyBlackListEntryUserAgent struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
Os string `json:"os"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Jabber основная структура-объект, содержащая стейты и проч.
|
||||||
Jabber struct {
|
Jabber struct {
|
||||||
Server string `json:"server,omitempty"`
|
// C - конфиг, как он распарсился из конфиг-файла.
|
||||||
Port int `json:"port,omitempty"`
|
C MyConfig
|
||||||
Ssl bool `json:"ssl,omitempty"`
|
|
||||||
StartTLS bool `json:"starttls,omitempty"`
|
|
||||||
SslVerify bool `json:"ssl_verify,omitempty"`
|
|
||||||
InsecureAllowUnencryptedAuth bool `json:"insecureallowunencryptedauth,omitempty"`
|
|
||||||
ConnectionTimeout int64 `json:"connection_timeout,omitempty"`
|
|
||||||
ReconnectDelay int64 `json:"reconnect_delay,omitempty"`
|
|
||||||
ServerPingDelay int64 `json:"server_ping_delay,omitempty"`
|
|
||||||
MucPingDelay int64 `json:"muc_ping_delay,omitempty"`
|
|
||||||
MucRejoinDelay int64 `json:"muc_rejoin_delay,omitempty"`
|
|
||||||
PingSplayDelay int64 `json:"ping_splay_delay,omitempty"`
|
|
||||||
Nick string `json:"nick,omitempty"`
|
|
||||||
Resource string `json:"resource,omitempty"`
|
|
||||||
User string `json:"user,omitempty"`
|
|
||||||
Password string `json:"password,omitempty"`
|
|
||||||
BotMasters []string `json:"bot_masters,omitempty"`
|
|
||||||
Channels []struct {
|
|
||||||
Name string `json:"name,omitempty"`
|
|
||||||
Nick string `json:"nick,omitempty"`
|
|
||||||
Password string `json:"password,omitempty"`
|
|
||||||
Bayes struct {
|
|
||||||
Enabled bool `json:"enabled,omitempty"`
|
|
||||||
MinWords int64 `json:"min_words,omitempty"`
|
|
||||||
MinLength int `json:"min_length,omitempty"`
|
|
||||||
DefaultAction string `json:"default_action,omitempty"`
|
|
||||||
} `json:"bayes,omitempty"`
|
|
||||||
AllCaps struct {
|
|
||||||
Enabled bool `json:"enabled,omitempty"`
|
|
||||||
MinLength int `json:"min_length,omitempty"`
|
|
||||||
DefaultAction string `json:"default_action,omitempty"`
|
|
||||||
} `json:"all_caps,omitempty"`
|
|
||||||
} `json:"channels"`
|
|
||||||
StartupStatus []string `json:"startup_status,omitempty"`
|
|
||||||
RuntimeStatus struct {
|
|
||||||
Text []string `json:"text,omitempty"`
|
|
||||||
RotationTime int64 `json:"rotation_time,omitempty"`
|
|
||||||
RotationSplayTime int64 `json:"rotation_splay_time,omitempty"`
|
|
||||||
} `json:"runtime_status,omitempty"`
|
|
||||||
BanDelay int64 `json:"ban_delay,omitempty"`
|
|
||||||
BanPhrasesEnable bool `json:"ban_phrases_enable,omitempty"`
|
|
||||||
BanPhrases []string `json:"ban_phrases,omitempty"`
|
|
||||||
} `json:"jabber,omitempty"`
|
|
||||||
|
|
||||||
CSign string `json:"csign,omitempty"`
|
// WhiteList - структурка с безусловно разрешёнными jid-ами.
|
||||||
Loglevel string `json:"loglevel,omitempty"`
|
WhiteList MyWhiteList
|
||||||
Log string `json:"log,omitempty"`
|
|
||||||
Version string `json:"version,omitempty"`
|
|
||||||
ExeName string `json:"exe_name,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// MyWhiteList прототип структурки с белым списком jid-ов.
|
// BlackList - структурка с запрещёнными по регуляркам фразами, никами, jid-ами.
|
||||||
type MyWhiteList struct {
|
BlackList MyBlackList
|
||||||
Whitelist []struct {
|
|
||||||
RoomName string `json:"room_name,omitempty"`
|
|
||||||
Jid []string `json:"jid,omitempty"`
|
|
||||||
WipeBans bool `json:"wipe_bans,omitempty"`
|
|
||||||
} `json:"whitelist,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// MyBlackList прототип структурки с чёрным списком jid-ов.
|
// Опции подключения к xmpp-серверу.
|
||||||
type MyBlackList struct {
|
Options *xmpp.Options
|
||||||
Blacklist []struct {
|
|
||||||
RoomName string `json:"room_name,omitempty"`
|
|
||||||
ReasonEnable bool `json:"reason_enable,omitempty"`
|
|
||||||
JidRe []string `json:"jid_re,omitempty"`
|
|
||||||
NickRe []string `json:"nick_re,omitempty"`
|
|
||||||
PhraseRe []string `json:"phrase_re,omitempty"`
|
|
||||||
UserAgent []struct {
|
|
||||||
Name string `json:"name,omitempty"`
|
|
||||||
Version string `json:"version,omitempty"`
|
|
||||||
Os string `json:"os,omitempty"`
|
|
||||||
} `json:"user_agent,omitempty"`
|
|
||||||
} `json:"blacklist,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Jabber основная структура-объект, содержащая стейты и проч.
|
// gTomb пул активных горутин.
|
||||||
type Jabber struct {
|
GTomb tomb.Tomb
|
||||||
// C - конфиг, как он распарсился из конфиг-файла.
|
|
||||||
C MyConfig
|
|
||||||
|
|
||||||
// WhiteList - структурка с безусловно разрешёнными jid-ами
|
// Talk основная структурка xmpp-клиента.
|
||||||
WhiteList MyWhiteList
|
Talk *xmpp.Client
|
||||||
|
|
||||||
// BlackList - структурка с запрещёнными по регуляркам фразами, никами, jid-ами.
|
// sync.Map-ка с капабилити сервера.
|
||||||
BlackList MyBlackList
|
ServerCapsList *Collection
|
||||||
|
|
||||||
// Опции подключения к xmpp-серверу.
|
// ServerCapsQueried показывает, были ли запрошены capabilities сервера.
|
||||||
Options *xmpp.Options
|
ServerCapsQueried bool
|
||||||
|
|
||||||
// gTomb пул активных горутин.
|
// Время последней активности, нужно для c2s пингов - посылаем пинги, только если давненько ничего не приходило с
|
||||||
GTomb tomb.Tomb
|
// сервера.
|
||||||
|
LastServerActivity int64
|
||||||
|
|
||||||
// Talk основная структурка xmpp-клиента.
|
// Время, когда был отправлен c2s ping.
|
||||||
Talk *xmpp.Client
|
ServerPingTimestampTx int64
|
||||||
|
|
||||||
// sync.Map-ка с капабилити сервера.
|
// Время, когда был принят s2c pong.
|
||||||
ServerCapsList *Collection
|
ServerPingTimestampRx int64
|
||||||
|
|
||||||
// ServerCapsQueried показывает, были ли запрошены capabilities сервера.
|
// Время последней активности, нужно для jabber:iq:last.
|
||||||
ServerCapsQueried bool
|
LastActivity int64
|
||||||
|
|
||||||
// Время последней активности, нужно для c2s пингов - посылаем пинги, только если давненько ничего не приходило с
|
// sync.Map-ка с комнатами и их capability.
|
||||||
// сервера.
|
MucCapsList *Collection
|
||||||
LastServerActivity int64
|
|
||||||
|
|
||||||
// Время, когда был отправлен c2s ping.
|
// Время последней активности MUC-ов, нужно для пингов - посылаем пинги, только если давненько ничего не приходило из
|
||||||
ServerPingTimestampTx int64
|
// muc-ов.
|
||||||
|
LastMucActivity *Collection
|
||||||
|
|
||||||
// Время, когда был принят s2c pong.
|
// Список комнат, в которых находится бот.
|
||||||
ServerPingTimestampRx int64
|
RoomsConnected []string
|
||||||
|
|
||||||
// Время последней активности, нужно для jabber:iq:last.
|
// sync.Map-ка со списком участников конференций (в json-формате, согласно структуре xmpp.Presence, "room".[]json).
|
||||||
LastActivity int64
|
RoomPresences *Collection
|
||||||
|
|
||||||
// sync.Map-ка с комнатами и их capability.
|
// Канал, по котором приходят сообщения о том, что ОС отправила некие сигналы процессу.
|
||||||
MucCapsList *Collection
|
SigChan chan os.Signal
|
||||||
|
|
||||||
// Время последней активности MUC-ов, нужно для пингов - посылаем пинги, только если давненько ничего не приходило из
|
// Индиктор того, что процесс завершается.
|
||||||
// muc-ов.
|
Shutdown bool
|
||||||
LastMucActivity *Collection
|
|
||||||
|
|
||||||
// Список комнат, в которых находится бот.
|
// Индиктор того, что соединение установлено.
|
||||||
RoomsConnected []string
|
IsConnected bool
|
||||||
|
|
||||||
// sync.Map-ка со списком участников конференций (в json-формате, согласно структуре xmpp.Presence, "room".[]json).
|
// Индикатор того, что соединение в процессе достукивания до сервера.
|
||||||
RoomPresences *Collection
|
Connecting bool
|
||||||
|
}
|
||||||
|
|
||||||
// Канал, по котором приходят сообщения о том, что ОС отправила некие сигналы процессу.
|
// SimpleIqGetQuery прототип структурки для разбора запросов xmpp discovery query, например,
|
||||||
SigChan chan os.Signal
|
// https://xmpp.org/extensions/xep-0030.html#example-18 .
|
||||||
|
SimpleIqGetQuery struct {
|
||||||
|
XMLName xml.Name `xml:"query"`
|
||||||
|
Text string `xml:",chardata"`
|
||||||
|
Xmlns string `xml:"xmlns,attr"`
|
||||||
|
Node string `xml:"node,attr,omitempty"` // для xmlns="http://jabber.org/protocol/disco#items"
|
||||||
|
}
|
||||||
|
|
||||||
// Индиктор того, что процесс завершается.
|
// PubsubIQGetQuery прототип структурки для разбора запросов xmpp pubsub.
|
||||||
Shutdown bool
|
PubsubIQGetQuery struct {
|
||||||
|
XMLName xml.Name `xml:"pubsub"`
|
||||||
|
Text string `xml:",chardata"`
|
||||||
|
Xmlns string `xml:"xmlns,attr"`
|
||||||
|
Items struct {
|
||||||
|
Text string `xml:",chardata"`
|
||||||
|
Node string `xml:"node,attr"`
|
||||||
|
MaxItems string `xml:"max_items,attr"`
|
||||||
|
} `xml:"items"`
|
||||||
|
}
|
||||||
|
|
||||||
// Индиктор того, что соединение установлено.
|
// TimeIqGetQuery прототип структурки для разбора IQ запросов на локальное время клиента,
|
||||||
IsConnected bool
|
// https://xmpp.org/extensions/xep-0202.html
|
||||||
|
TimeIqGetQuery struct {
|
||||||
|
// <time xmlns="urn:xmpp:time"/>.
|
||||||
|
XMLName xml.Name `xml:"time"`
|
||||||
|
Text string `xml:",chardata"`
|
||||||
|
Xmlns string `xml:"xmlns,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
// Индикатор того, что соединение в процессе достукивания до сервера.
|
// IqPing прототип структурки для разбора IQ запросов на пинг клиента, https://xmpp.org/extensions/xep-0199.html
|
||||||
Connecting bool
|
IqPing struct {
|
||||||
}
|
XMLName xml.Name `xml:"ping"`
|
||||||
|
Text string `xml:",chardata"`
|
||||||
|
Xmlns string `xml:"xmlns,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
// SimpleIqGetQuery прототип структурки для разбора запросов xmpp discovery query, например,
|
// IqErrorCancelNotAcceptable прототип структурки для разбора IQ ответов, когда сервис (сервер, клиент, etc) не
|
||||||
// https://xmpp.org/extensions/xep-0030.html#example-18 .
|
// может или не хочет принимать наш iq-запрос https://xmpp.org/extensions/xep-0099.html
|
||||||
type SimpleIqGetQuery struct {
|
IqErrorCancelNotAcceptable struct {
|
||||||
XMLName xml.Name `xml:"query"`
|
XMLName xml.Name `xml:"error"`
|
||||||
Text string `xml:",chardata"`
|
Text string `xml:",chardata"`
|
||||||
Xmlns string `xml:"xmlns,attr"`
|
Type string `xml:"type,attr"`
|
||||||
Node string `xml:"node,attr,omitempty"` // для xmlns="http://jabber.org/protocol/disco#items"
|
By string `xml:"by,attr"`
|
||||||
}
|
NotAcceptable struct {
|
||||||
|
Text string `xml:",chardata"`
|
||||||
|
Xmlns string `xml:"xmlns,attr"`
|
||||||
|
} `xml:"not-acceptable"`
|
||||||
|
}
|
||||||
|
|
||||||
// PubsubIQGetQuery прототип структурки для разбора запросов xmpp pubsub.
|
// IqErrorCancelServiceNotAvailable обычно возвращается сервером в ответ на iq-запрос к несуществующей сущности.
|
||||||
type PubsubIQGetQuery struct {
|
// Если мы такое получаем от клиента, значит мы запросили что-то, что клиент явно не объявил как существущее в
|
||||||
XMLName xml.Name `xml:"pubsub"`
|
// списке своих capability. https://xmpp.org/extensions/xep-0030.html#errors
|
||||||
Text string `xml:",chardata"`
|
IqErrorCancelServiceNotAvailable struct {
|
||||||
Xmlns string `xml:"xmlns,attr"`
|
XMLName xml.Name `xml:"error"`
|
||||||
Items struct {
|
Text string `xml:",chardata"`
|
||||||
Text string `xml:",chardata"`
|
Type string `xml:"type,attr"`
|
||||||
Node string `xml:"node,attr"`
|
ServiceUnavailable struct {
|
||||||
MaxItems string `xml:"max_items,attr"`
|
Text string `xml:",chardata"`
|
||||||
} `xml:"items"`
|
Xmlns string `xml:"xmlns,attr"`
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TimeIqGetQuery прототип структурки для разбора IQ запросов на локальное время клиента,
|
/* <iq from='slackware-current@conference.jabber.ru/Batman46(new)' to='buny@jabber.ru/Лунная призма' type='error' id='94e55122-6
|
||||||
// https://xmpp.org/extensions/xep-0202.html
|
c15-4e88-a0df-ae9f02e0b550'>
|
||||||
type TimeIqGetQuery struct {
|
<query xmlns='jabber:iq:version'/>
|
||||||
// <time xmlns="urn:xmpp:time"/>
|
<error code='404' type='cancel'>
|
||||||
XMLName xml.Name `xml:"time"`
|
<item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
|
||||||
Text string `xml:",chardata"`
|
<text xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'>Адресата нет в конференции</text>
|
||||||
Xmlns string `xml:"xmlns,attr"`
|
</error>
|
||||||
}
|
</iq>
|
||||||
|
*/
|
||||||
|
|
||||||
// IqPing прототип структурки для разбора IQ запросов на пинг клиента, https://xmpp.org/extensions/xep-0199.html
|
// IqErrorCancelItemNotFound не знаю, насколько корректно так отвечать на запрос версии ПО, но такое иногда
|
||||||
type IqPing struct {
|
// встречаетсяю. Придётся поддержать.
|
||||||
XMLName xml.Name `xml:"ping"`
|
IqErrorCancelItemNotFound struct {
|
||||||
Text string `xml:",chardata"`
|
XMLName xml.Name `xml:"error"`
|
||||||
Xmlns string `xml:"xmlns,attr"`
|
Chardata string `xml:",chardata"`
|
||||||
}
|
Code string `xml:"code,attr"`
|
||||||
|
Type string `xml:"type,attr"`
|
||||||
|
ItemNotFound struct {
|
||||||
|
Text string `xml:",chardata"`
|
||||||
|
Xmlns string `xml:"xmlns,attr"`
|
||||||
|
} `xml:"item-not-found"`
|
||||||
|
Text struct {
|
||||||
|
Text string `xml:",chardata"`
|
||||||
|
Xmlns string `xml:"xmlns,attr"`
|
||||||
|
} `xml:"text"`
|
||||||
|
}
|
||||||
|
|
||||||
// IqErrorCancelNotAcceptable прототип структурки для разбора IQ ответов, когда сервис (сервер, клиент, etc) не
|
// IqResultSoftwareVersion прототип структурки для разбора IQ ответов на запрос о названии и версии клиентского ПО.
|
||||||
// может или не хочет принимать наш iq-запрос https://xmpp.org/extensions/xep-0099.html
|
IqResultSoftwareVersion struct {
|
||||||
type IqErrorCancelNotAcceptable struct {
|
XMLName xml.Name `xml:"query"`
|
||||||
XMLName xml.Name `xml:"error"`
|
Text string `xml:",chardata"`
|
||||||
Text string `xml:",chardata"`
|
Xmlns string `xml:"xmlns,attr"`
|
||||||
Type string `xml:"type,attr"`
|
Name string `xml:"name"`
|
||||||
By string `xml:"by,attr"`
|
Version string `xml:"version"`
|
||||||
NotAcceptable struct {
|
Os string `xml:"os,omitempty"`
|
||||||
Text string `xml:",chardata"`
|
}
|
||||||
Xmlns string `xml:"xmlns,attr"`
|
)
|
||||||
} `xml:"not-acceptable"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// IqResultSoftwareVersion прототип структурки для разбора IQ ответов на запрос о названии и версии клиентского ПО.
|
|
||||||
type IqResultSoftwareVersion struct {
|
|
||||||
XMLName xml.Name `xml:"query"`
|
|
||||||
Text string `xml:",chardata"`
|
|
||||||
Xmlns string `xml:"xmlns,attr"`
|
|
||||||
Name string `xml:"name"`
|
|
||||||
Version string `xml:"version"`
|
|
||||||
Os string `xml:"os,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package jabber
|
package jabber
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"buny-jabber-bot/internal/log"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
@@ -9,8 +10,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/eleksir/go-xmpp"
|
"github.com/xmppo/go-xmpp"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// RandomPhrase выдаёт одну рандомную фразу из даденного списка фраз.
|
// RandomPhrase выдаёт одну рандомную фразу из даденного списка фраз.
|
||||||
@@ -26,16 +26,16 @@ func RandomPhrase(list []string) string {
|
|||||||
|
|
||||||
// InterfaceToStringSlice превращает данный интерфейс в слайс строк.
|
// InterfaceToStringSlice превращает данный интерфейс в слайс строк.
|
||||||
// Если может, конечно :) .
|
// Если может, конечно :) .
|
||||||
func InterfaceToStringSlice(iface interface{}) []string {
|
func InterfaceToStringSlice(iface any) []string {
|
||||||
var mySlice []string
|
var mySlice []string
|
||||||
|
|
||||||
// А теперь мы начинаем дурдом, нам надо превратить ёбанный interface{} в []string
|
// А теперь мы начинаем дурдом, нам надо превратить ёбанный any в []string.
|
||||||
// Поскольку interface{} может быть чем угодно, перестрахуемся
|
// Поскольку any может быть чем угодно, перестрахуемся.
|
||||||
if reflect.TypeOf(iface).Kind() == reflect.Slice {
|
if reflect.TypeOf(iface).Kind() == reflect.Slice {
|
||||||
shit := reflect.ValueOf(iface)
|
shit := reflect.ValueOf(iface)
|
||||||
|
|
||||||
for i := 0; i < shit.Len(); i++ {
|
for v := range shit.Seq() {
|
||||||
mySlice = append(mySlice, fmt.Sprint(shit.Index(i)))
|
mySlice = append(mySlice, v.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,23 +45,30 @@ func InterfaceToStringSlice(iface interface{}) []string {
|
|||||||
// GetRealJIDfromNick достаёт из запомненных presence-ов по даденному nick-у real jid с resource-ом. Nick должен
|
// GetRealJIDfromNick достаёт из запомненных presence-ов по даденному nick-у real jid с resource-ом. Nick должен
|
||||||
// содержать имя конфы, откуда участник.
|
// содержать имя конфы, откуда участник.
|
||||||
func (j *Jabber) GetRealJIDfromNick(fullNick string) string {
|
func (j *Jabber) GetRealJIDfromNick(fullNick string) string {
|
||||||
var p xmpp.Presence
|
var (
|
||||||
|
presenceJSON []byte
|
||||||
|
ok bool
|
||||||
|
presences []xmpp.Presence
|
||||||
|
)
|
||||||
|
|
||||||
room := (strings.SplitN(fullNick, "/", 2))[0]
|
room := (strings.SplitN(fullNick, "/", 2))[0]
|
||||||
|
|
||||||
// Достанем presence участника
|
if presenceJSONInterface, present := j.RoomPresences.Get(room); present {
|
||||||
presenceJSONInterface, present := j.RoomPresences.Get(room)
|
presenceJSON, ok = presenceJSONInterface.([]byte)
|
||||||
|
if !ok {
|
||||||
|
log.Errorf("Looks like we have corrupted entry in presence db")
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(presenceJSON, &presences); err != nil {
|
||||||
|
log.Errorf("Looks like we have corrupted entry in presence db")
|
||||||
|
|
||||||
// Никого нет дома
|
|
||||||
if !present {
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
presenceJSONStrings := InterfaceToStringSlice(presenceJSONInterface)
|
for _, p := range presences { //nolint: gocritic // Развели тут, блять, страну советов, напиши что надо сделать кодом, сука.
|
||||||
|
|
||||||
for _, presepresenceJSONString := range presenceJSONStrings {
|
|
||||||
_ = json.Unmarshal([]byte(presepresenceJSONString), &p)
|
|
||||||
|
|
||||||
if p.From == fullNick {
|
if p.From == fullNick {
|
||||||
return p.JID
|
return p.JID
|
||||||
}
|
}
|
||||||
@@ -85,8 +92,9 @@ func (j *Jabber) GetBotNickFromRoomConfig(room string) string {
|
|||||||
// RotateStatus периодически изменяет статус бота в MUC-е согласно настройкам из кофига.
|
// RotateStatus периодически изменяет статус бота в MUC-е согласно настройкам из кофига.
|
||||||
func (j *Jabber) RotateStatus(room string) error {
|
func (j *Jabber) RotateStatus(room string) error {
|
||||||
for {
|
for {
|
||||||
// TODO: Переделать на ticker-ы
|
// TODO: Переделать на ticker-ы.
|
||||||
totalSleepTime := time.Duration(j.C.Jabber.RuntimeStatus.RotationTime) * time.Second
|
totalSleepTime := time.Duration(j.C.Jabber.RuntimeStatus.RotationTime) * time.Second
|
||||||
|
|
||||||
totalSleepTime += time.Duration(j.C.Jabber.RuntimeStatus.RotationSplayTime) * time.Second
|
totalSleepTime += time.Duration(j.C.Jabber.RuntimeStatus.RotationSplayTime) * time.Second
|
||||||
|
|
||||||
for {
|
for {
|
||||||
@@ -124,14 +132,14 @@ func (j *Jabber) RotateStatus(room string) error {
|
|||||||
func (j *Jabber) JoinMuc(room string) error {
|
func (j *Jabber) JoinMuc(room string) error {
|
||||||
log.Debugf("Sending disco#info from %s to %s", j.Talk.JID(), room)
|
log.Debugf("Sending disco#info from %s to %s", j.Talk.JID(), room)
|
||||||
|
|
||||||
if _, err := j.Talk.DiscoverInfo(j.Talk.JID(), room); err != nil {
|
if _, err := j.Talk.DiscoverInfo(j.Talk.JID()); err != nil {
|
||||||
return fmt.Errorf("unable to send disco#info to MUC %s: %w", room, err)
|
return fmt.Errorf("unable to send disco#info to MUC %s: %w", room, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ждём, пока muc нам вернёт список фичей.
|
// Ждём, пока muc нам вернёт список фичей.
|
||||||
for i := 0; i < (20 * int(j.C.Jabber.ConnectionTimeout)); i++ {
|
for range 20 * int(j.C.Jabber.ConnectionTimeout) {
|
||||||
var (
|
var (
|
||||||
myRoom interface{}
|
myRoom any
|
||||||
supported bool
|
supported bool
|
||||||
exist bool
|
exist bool
|
||||||
)
|
)
|
||||||
@@ -154,7 +162,7 @@ func (j *Jabber) JoinMuc(room string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Заходим в конфу, наконец-то
|
// Заходим в конфу, наконец-то.
|
||||||
if _, err := j.Talk.JoinMUCNoHistory(room, j.GetBotNickFromRoomConfig(room)); err != nil {
|
if _, err := j.Talk.JoinMUCNoHistory(room, j.GetBotNickFromRoomConfig(room)); err != nil {
|
||||||
return fmt.Errorf("unable to join to MUC: %s, %w", room, err)
|
return fmt.Errorf("unable to join to MUC: %s, %w", room, err)
|
||||||
}
|
}
|
||||||
@@ -164,7 +172,7 @@ func (j *Jabber) JoinMuc(room string) error {
|
|||||||
// Ждём, когда прилетит presence из комнаты, тогда мы точно знаем, что мы вошли.
|
// Ждём, когда прилетит presence из комнаты, тогда мы точно знаем, что мы вошли.
|
||||||
entered := false
|
entered := false
|
||||||
|
|
||||||
for i := 0; i < (20 * int(j.C.Jabber.ConnectionTimeout)); i++ {
|
for range 20 * int(j.C.Jabber.ConnectionTimeout) {
|
||||||
time.Sleep(50 * time.Millisecond)
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
|
||||||
if slices.Contains(j.RoomsConnected, room) {
|
if slices.Contains(j.RoomsConnected, room) {
|
||||||
@@ -189,18 +197,30 @@ func (j *Jabber) JoinMuc(room string) error {
|
|||||||
|
|
||||||
j.GTomb.Go(func() error { return j.RotateStatus(room) })
|
j.GTomb.Go(func() error { return j.RotateStatus(room) })
|
||||||
|
|
||||||
// Время проверить участников на предмет злобности
|
// Время проверить участников на предмет злобности.
|
||||||
namesInterface, present := j.RoomPresences.Get(room)
|
presenceJSONInterface, present := j.RoomPresences.Get(room)
|
||||||
|
|
||||||
// Если room есть в списке presence-ов, то фигачим. room там должен быть, просто обязан.
|
|
||||||
if present {
|
if present {
|
||||||
for _, name := range InterfaceToStringSlice(namesInterface) {
|
presenceJSON, ok := presenceJSONInterface.([]byte)
|
||||||
var v xmpp.Presence
|
|
||||||
|
|
||||||
_ = json.Unmarshal([]byte(name), &v)
|
if !ok {
|
||||||
log.Infof("Fake presence forged for %s just for on-enter check", name)
|
log.Errorf("Looks like we have corrupted entry in presence db")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var presences []xmpp.Presence
|
||||||
|
|
||||||
|
if err := json.Unmarshal(presenceJSON, &presences); err != nil {
|
||||||
|
log.Errorf("Looks like we have corrupted entry in presence db")
|
||||||
|
|
||||||
|
return nil //nolint: nilerr // If presence db is corrupted it is a bad thing, but we log it anyway.
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range presences { //nolint: gocritic // Развели тут, блять, страну советов, напиши что надо сделать кодом, сука.
|
||||||
|
log.Infof("Fake presence forged for %s just for on-enter check", p.JID)
|
||||||
// Оно там внутри всё само обработает, если вдруг возникнет wire error, то зарекконетится.
|
// Оно там внутри всё само обработает, если вдруг возникнет wire error, то зарекконетится.
|
||||||
_ = j.BunyPresense(v)
|
_ = j.BunyPresense(p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,6 +241,7 @@ func (j *Jabber) EstablishConnection() error {
|
|||||||
j.RoomsConnected = make([]string, 0)
|
j.RoomsConnected = make([]string, 0)
|
||||||
|
|
||||||
log.Debugf("Establishing connection to %s", j.Options.Host)
|
log.Debugf("Establishing connection to %s", j.Options.Host)
|
||||||
|
|
||||||
j.Talk, err = j.Options.NewClient()
|
j.Talk, err = j.Options.NewClient()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -234,7 +255,7 @@ func (j *Jabber) EstablishConnection() error {
|
|||||||
|
|
||||||
log.Info("Connected")
|
log.Info("Connected")
|
||||||
|
|
||||||
// Джойнимся к чятикам, но делаем это в фоне, чтобы не блочиться на ошибках, например, если бота забанили
|
// Джойнимся к чятикам, но делаем это в фоне, чтобы не блочиться на ошибках, например, если бота забанили.
|
||||||
for _, roomStruct := range j.C.Jabber.Channels {
|
for _, roomStruct := range j.C.Jabber.Channels {
|
||||||
room := roomStruct.Name
|
room := roomStruct.Name
|
||||||
|
|
||||||
@@ -249,7 +270,7 @@ func (j *Jabber) EstablishConnection() error {
|
|||||||
|
|
||||||
log.Debugf("Sending disco#info to %s", j.C.Jabber.Server)
|
log.Debugf("Sending disco#info to %s", j.C.Jabber.Server)
|
||||||
|
|
||||||
_, err = j.Talk.DiscoverInfo(j.Talk.JID(), j.C.Jabber.Server)
|
_, err = j.Talk.DiscoverInfo(j.Talk.JID())
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to send disco#info to jabber server: %w", err)
|
return fmt.Errorf("unable to send disco#info to jabber server: %w", err)
|
||||||
@@ -268,6 +289,7 @@ func (j *Jabber) ProbeServerLiveness() error { //nolint:gocognit
|
|||||||
}
|
}
|
||||||
|
|
||||||
sleepTime := time.Duration(j.C.Jabber.ServerPingDelay) * 1000 * time.Millisecond
|
sleepTime := time.Duration(j.C.Jabber.ServerPingDelay) * 1000 * time.Millisecond
|
||||||
|
|
||||||
sleepTime += time.Duration(rand.Int63n(1000*j.C.Jabber.PingSplayDelay)) * time.Millisecond
|
sleepTime += time.Duration(rand.Int63n(1000*j.C.Jabber.PingSplayDelay)) * time.Millisecond
|
||||||
time.Sleep(sleepTime)
|
time.Sleep(sleepTime)
|
||||||
|
|
||||||
@@ -276,26 +298,25 @@ func (j *Jabber) ProbeServerLiveness() error { //nolint:gocognit
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Пингуем, только если не было никакой активности в течение > config.Jabber.ServerPingDelay,
|
// Пингуем, только если не было никакой активности в течение > config.Jabber.ServerPingDelay,
|
||||||
// в худшем случе это будет ~ (config.Jabber.PingSplayDelay * 2) + config.Jabber.PingSplayDelay
|
// в худшем случе это будет ~ (config.Jabber.PingSplayDelay * 2) + config.Jabber.PingSplayDelay.
|
||||||
// if (time.Now().Unix() - lastServerActivity) < (config.Jabber.ServerPingDelay + config.Jabber.PingSplayDelay) {
|
|
||||||
// continue
|
|
||||||
// }
|
|
||||||
|
|
||||||
if j.ServerCapsQueried { // Сервер ответил на disco#info
|
if j.ServerCapsQueried { // Сервер ответил на disco#info.
|
||||||
var (
|
var (
|
||||||
value interface{}
|
value any
|
||||||
exist bool
|
exist bool
|
||||||
)
|
)
|
||||||
|
|
||||||
value, exist = j.ServerCapsList.Get("urn:xmpp:ping")
|
value, exist = j.ServerCapsList.Get("urn:xmpp:ping")
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
// Сервер анонсировал, что умеет в c2s пинги
|
// Сервер анонсировал, что умеет в c2s пинги.
|
||||||
case exist && value.(bool):
|
case exist && value.(bool):
|
||||||
// Таймаут c2s пинга. Возьмём сумму задержки между пингами, добавим таймаут коннекта и добавим
|
// Таймаут c2s пинга. Возьмём сумму задержки между пингами, добавим таймаут коннекта и добавим
|
||||||
// максимальную корректировку разброса.
|
// максимальную корректировку разброса.
|
||||||
txTimeout := j.C.Jabber.ServerPingDelay + j.C.Jabber.ConnectionTimeout
|
txTimeout := j.C.Jabber.ServerPingDelay + j.C.Jabber.ConnectionTimeout
|
||||||
|
|
||||||
txTimeout += j.C.Jabber.PingSplayDelay
|
txTimeout += j.C.Jabber.PingSplayDelay
|
||||||
|
|
||||||
rxTimeout := txTimeout
|
rxTimeout := txTimeout
|
||||||
|
|
||||||
rxTimeAgo := time.Now().Unix() - j.ServerPingTimestampRx
|
rxTimeAgo := time.Now().Unix() - j.ServerPingTimestampRx
|
||||||
@@ -311,9 +332,11 @@ func (j *Jabber) ProbeServerLiveness() error { //nolint:gocognit
|
|||||||
|
|
||||||
return err
|
return err
|
||||||
|
|
||||||
// По-умолчанию, мы отправляем c2s пинг
|
// По-умолчанию, мы отправляем c2s пинг.
|
||||||
default:
|
default:
|
||||||
log.Debugf("Sending c2s ping from %s to %s", j.Talk.JID(), j.C.Jabber.Server)
|
if j.C.Debug.Ping {
|
||||||
|
log.Debugf("Sending c2s ping from %s to %s", j.Talk.JID(), j.C.Jabber.Server)
|
||||||
|
}
|
||||||
|
|
||||||
if err := j.Talk.PingC2S(j.Talk.JID(), j.C.Jabber.Server); err != nil {
|
if err := j.Talk.PingC2S(j.Talk.JID(), j.C.Jabber.Server); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -321,8 +344,10 @@ func (j *Jabber) ProbeServerLiveness() error { //nolint:gocognit
|
|||||||
|
|
||||||
j.ServerPingTimestampTx = time.Now().Unix()
|
j.ServerPingTimestampTx = time.Now().Unix()
|
||||||
}
|
}
|
||||||
} else { // Первая пуля пока не вылетела, отправляем
|
} else { // Первая пуля пока не вылетела, отправляем.
|
||||||
log.Debugf("Sending first c2s ping from %s to %s", j.Talk.JID(), j.C.Jabber.Server)
|
if j.C.Debug.Ping {
|
||||||
|
log.Debugf("Sending first c2s ping from %s to %s", j.Talk.JID(), j.C.Jabber.Server)
|
||||||
|
}
|
||||||
|
|
||||||
if err := j.Talk.PingC2S(j.Talk.JID(), j.C.Jabber.Server); err != nil {
|
if err := j.Talk.PingC2S(j.Talk.JID(), j.C.Jabber.Server); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -331,16 +356,20 @@ func (j *Jabber) ProbeServerLiveness() error { //nolint:gocognit
|
|||||||
j.ServerPingTimestampTx = time.Now().Unix()
|
j.ServerPingTimestampTx = time.Now().Unix()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Сервер не анонсировал, что умеет в c2s пинги
|
// Сервер не анонсировал, что умеет в c2s пинги.
|
||||||
default:
|
default:
|
||||||
log.Debug("Sending keepalive whitespace ping")
|
if j.C.Debug.Ping {
|
||||||
|
log.Debug("Sending keepalive whitespace ping")
|
||||||
|
}
|
||||||
|
|
||||||
if _, err := j.Talk.SendKeepAlive(); err != nil {
|
if _, err := j.Talk.SendKeepAlive(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else { // Сервер не ответил на disco#info
|
} else { // Сервер не ответил на disco#info.
|
||||||
log.Debug("Sending keepalive whitespace ping")
|
if j.C.Debug.Ping {
|
||||||
|
log.Debug("Sending keepalive whitespace ping")
|
||||||
|
}
|
||||||
|
|
||||||
if _, err := j.Talk.SendKeepAlive(); err != nil {
|
if _, err := j.Talk.SendKeepAlive(); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -358,7 +387,7 @@ func (j *Jabber) ProbeMUCLiveness() { //nolint:gocognit
|
|||||||
for _, room := range j.RoomsConnected {
|
for _, room := range j.RoomsConnected {
|
||||||
var (
|
var (
|
||||||
exist bool
|
exist bool
|
||||||
lastActivityTS interface{}
|
lastActivityTS any
|
||||||
)
|
)
|
||||||
|
|
||||||
// Если записи про комнату нету, то пинговать её бессмысленно.
|
// Если записи про комнату нету, то пинговать её бессмысленно.
|
||||||
@@ -393,7 +422,7 @@ func (j *Jabber) ProbeMUCLiveness() { //nolint:gocognit
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
*/
|
*/
|
||||||
var roomMap interface{}
|
var roomMap any
|
||||||
|
|
||||||
roomMap, exist = j.MucCapsList.Get(room)
|
roomMap, exist = j.MucCapsList.Get(room)
|
||||||
|
|
||||||
@@ -406,7 +435,9 @@ func (j *Jabber) ProbeMUCLiveness() { //nolint:gocognit
|
|||||||
sleepTime := time.Duration(rand.Int63n(1000*j.C.Jabber.PingSplayDelay)) * time.Millisecond
|
sleepTime := time.Duration(rand.Int63n(1000*j.C.Jabber.PingSplayDelay)) * time.Millisecond
|
||||||
time.Sleep(sleepTime)
|
time.Sleep(sleepTime)
|
||||||
|
|
||||||
log.Debugf("Sending MUC ping from %s to %s", j.Talk.JID(), room)
|
if j.C.Debug.Ping {
|
||||||
|
log.Debugf("Sending MUC ping from %s to %s", j.Talk.JID(), room)
|
||||||
|
}
|
||||||
|
|
||||||
if e := j.Talk.PingS2S(j.Talk.JID(), room); e != nil {
|
if e := j.Talk.PingS2S(j.Talk.JID(), room); e != nil {
|
||||||
err = fmt.Errorf("unable to ping MUC %s: %w", room, e)
|
err = fmt.Errorf("unable to ping MUC %s: %w", room, e)
|
||||||
|
|||||||
232
internal/log/log.go
Normal file
232
internal/log/log.go
Normal file
@@ -0,0 +1,232 @@
|
|||||||
|
// Package log wraps log/slog for more easier and convinient use.
|
||||||
|
package log
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"log/slog"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Handler exposes slog handler struct.
|
||||||
|
Handler slog.Handler
|
||||||
|
|
||||||
|
// Ctx log context. For now it is just sits here and do nothing.
|
||||||
|
Ctx = context.Background()
|
||||||
|
|
||||||
|
// Writer go-writer eported for capturing other types of logs, even for std log go facility.
|
||||||
|
Writer io.Writer
|
||||||
|
|
||||||
|
// reader is required for Writer to work properly. It is connected via io.Pipe() to Writer.
|
||||||
|
reader io.Reader
|
||||||
|
|
||||||
|
// Logger *log.Logger thing required for compatibility with old-good log package.
|
||||||
|
Logger log.Logger
|
||||||
|
|
||||||
|
// LogFileHandler is currently opened log file descriptor. Stderr by default.
|
||||||
|
LogFileHandler = os.Stderr
|
||||||
|
|
||||||
|
// LogLevel current loglevel. Info by default.
|
||||||
|
LogLevel = slog.LevelInfo
|
||||||
|
)
|
||||||
|
|
||||||
|
// Init setup logger stuff.
|
||||||
|
// level can be error, warn, info, debug if something other supplied info level selected.
|
||||||
|
// fileDescriptor should be opened before supplying it to Init().
|
||||||
|
func Init(level string, fileDescriptor *os.File) {
|
||||||
|
LogFileHandler = fileDescriptor
|
||||||
|
|
||||||
|
// no panic, no trace.
|
||||||
|
switch level {
|
||||||
|
case "debug":
|
||||||
|
LogLevel = slog.LevelDebug
|
||||||
|
|
||||||
|
case "info":
|
||||||
|
LogLevel = slog.LevelInfo
|
||||||
|
|
||||||
|
case "warn":
|
||||||
|
LogLevel = slog.LevelWarn
|
||||||
|
|
||||||
|
case "error":
|
||||||
|
LogLevel = slog.LevelError
|
||||||
|
|
||||||
|
default:
|
||||||
|
LogLevel = slog.LevelInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := &slog.HandlerOptions{ //nolint: exhaustruct
|
||||||
|
// Use the ReplaceAttr function on the handler options
|
||||||
|
// to be able to replace any single attribute in the log output.
|
||||||
|
ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr { //nolint: revive
|
||||||
|
// Check that we are handling the time key.
|
||||||
|
if a.Key != slog.TimeKey {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
t := a.Value.Time()
|
||||||
|
|
||||||
|
// change the value from a time.Time to a String
|
||||||
|
// where the string has the correct time format.
|
||||||
|
a.Value = slog.StringValue(t.Format(time.DateTime))
|
||||||
|
|
||||||
|
return a
|
||||||
|
},
|
||||||
|
|
||||||
|
Level: LogLevel,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Documentation says that we can use *os.File in place of io.Writer, but in this case it did not work.
|
||||||
|
// So we have to cast fileDescriptor to io.Writer.
|
||||||
|
w := io.Writer(fileDescriptor)
|
||||||
|
|
||||||
|
Handler = slog.NewTextHandler(w, opts)
|
||||||
|
|
||||||
|
slog.SetDefault(
|
||||||
|
slog.New(Handler),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Catch log.Logger message at info loglevel of slog logging facility.
|
||||||
|
Logger = *slog.NewLogLogger(Handler, slog.LevelError)
|
||||||
|
|
||||||
|
reader, Writer = io.Pipe()
|
||||||
|
|
||||||
|
go readIoPipe()
|
||||||
|
}
|
||||||
|
|
||||||
|
// readIoPipe reads pipe and writes it to slog.
|
||||||
|
func readIoPipe() {
|
||||||
|
scanner := bufio.NewScanner(reader)
|
||||||
|
|
||||||
|
for {
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text() // Get the current line as a string.
|
||||||
|
slog.Debug(line)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
// io.EOF is expected when done reading, e.g. on exit.
|
||||||
|
if errors.Is(err, io.EOF) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// something weird happen, we should it log to guess later what it was.
|
||||||
|
Errorf("Error during scanning log writer pipe: %v\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLevel returns current loglevel as string.
|
||||||
|
func GetLevel() string {
|
||||||
|
switch {
|
||||||
|
case Handler.Enabled(Ctx, slog.LevelDebug):
|
||||||
|
return "debug"
|
||||||
|
|
||||||
|
case Handler.Enabled(Ctx, slog.LevelInfo):
|
||||||
|
return "info"
|
||||||
|
|
||||||
|
case Handler.Enabled(Ctx, slog.LevelWarn):
|
||||||
|
return "warn"
|
||||||
|
|
||||||
|
case Handler.Enabled(Ctx, slog.LevelError):
|
||||||
|
return "error"
|
||||||
|
|
||||||
|
default:
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fatal passes message directly to slog.Error() and exits with error code 1.
|
||||||
|
func Fatal(message string) {
|
||||||
|
slog.Error(message)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fatalf logs message on error log level and exits with error code 1. It allows to supply arguments in printf() style .
|
||||||
|
func Fatalf(format string, a ...any) {
|
||||||
|
slog.Error(fmt.Sprintf(format, a...))
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error passes message directly to slog.Error().
|
||||||
|
func Error(message string) {
|
||||||
|
slog.Error(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Errorf logs message on error log level and allows to supply arguments in printf() style.
|
||||||
|
func Errorf(format string, a ...any) {
|
||||||
|
slog.Error(fmt.Sprintf(format, a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warn passes message directly to slog.Warn().
|
||||||
|
func Warn(message string) {
|
||||||
|
slog.Warn(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warnf logs message on warn log level and allows to supply arguments in printf() style.
|
||||||
|
func Warnf(format string, a ...any) {
|
||||||
|
slog.Warn(fmt.Sprintf(format, a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info passes message directly to slog.Info().
|
||||||
|
func Info(message string) {
|
||||||
|
slog.Info(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Infof logs message on info log level and allows to supply arguments in printf() style.
|
||||||
|
func Infof(format string, a ...any) {
|
||||||
|
slog.Info(fmt.Sprintf(format, a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug passes message directly to slog.Debug().
|
||||||
|
func Debug(message string) {
|
||||||
|
slog.Debug(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debugf logs message on debug log level and allows to supply arguments in printf() style.
|
||||||
|
func Debugf(format string, a ...any) {
|
||||||
|
slog.Debug(fmt.Sprintf(format, a...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes opened log file descriptors.
|
||||||
|
func Close() {
|
||||||
|
LogFileHandler.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReOpenLog closes and opens log file again.
|
||||||
|
func ReOpenLog() {
|
||||||
|
filename := LogFileHandler.Name()
|
||||||
|
|
||||||
|
// If we writing to stderr, we should not re-open it.
|
||||||
|
if filename == os.Stderr.Name() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
loglevel := GetLevel()
|
||||||
|
|
||||||
|
LogFileHandler.Close()
|
||||||
|
|
||||||
|
file, err := os.OpenFile(filename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
// Try to emulate our own log message format.
|
||||||
|
fmt.Fprintf(
|
||||||
|
os.Stderr,
|
||||||
|
"time=\"%s\" level=ERROR msg=\"Unable to open logfile %s: %s\"",
|
||||||
|
time.Now().Format(time.DateTime),
|
||||||
|
filename,
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Init(loglevel, file)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* vim: set ft=go noet ai ts=4 sw=4 sts=4: */
|
||||||
66
vendor/github.com/eleksir/go-xmpp/.golangci.yml
generated
vendored
66
vendor/github.com/eleksir/go-xmpp/.golangci.yml
generated
vendored
@@ -1,66 +0,0 @@
|
|||||||
run:
|
|
||||||
tests: false
|
|
||||||
skip-dirs:
|
|
||||||
- _example
|
|
||||||
modules-download-mode: readonly
|
|
||||||
allow-parallel-runners: true
|
|
||||||
go: '1.21'
|
|
||||||
output:
|
|
||||||
sort-results: true
|
|
||||||
linters-settings:
|
|
||||||
gosec:
|
|
||||||
excludes:
|
|
||||||
# Оно не используется для повышения безопасности или каких-то таких секъюрых вещей, оно используется для выборки
|
|
||||||
# произвольного элемента из массива, так что ИДИ НАХУЙ с этой проверкой и не еби мозги липовой безопасностью.
|
|
||||||
- G404 # Insecure random number source (rand)
|
|
||||||
linters:
|
|
||||||
disable-all: true
|
|
||||||
enable:
|
|
||||||
- errcheck
|
|
||||||
- gosimple
|
|
||||||
- govet
|
|
||||||
- ineffassign
|
|
||||||
- staticcheck
|
|
||||||
- unused
|
|
||||||
- asasalint
|
|
||||||
- asciicheck
|
|
||||||
- bodyclose
|
|
||||||
- containedctx
|
|
||||||
- contextcheck
|
|
||||||
- decorder
|
|
||||||
- durationcheck
|
|
||||||
- errchkjson
|
|
||||||
- errorlint
|
|
||||||
- exhaustive
|
|
||||||
- exportloopref
|
|
||||||
- gci
|
|
||||||
- ginkgolinter
|
|
||||||
- gocheckcompilerdirectives
|
|
||||||
- gocritic
|
|
||||||
- godot
|
|
||||||
- gofmt
|
|
||||||
- goimports
|
|
||||||
- gosec
|
|
||||||
- gosmopolitan
|
|
||||||
- grouper
|
|
||||||
- importas
|
|
||||||
- ireturn
|
|
||||||
- makezero
|
|
||||||
- mirror
|
|
||||||
- nakedret
|
|
||||||
- nilerr
|
|
||||||
- nilnil
|
|
||||||
- nlreturn
|
|
||||||
- noctx
|
|
||||||
- nolintlint
|
|
||||||
- nonamedreturns
|
|
||||||
- predeclared
|
|
||||||
- promlinter
|
|
||||||
- reassign
|
|
||||||
- revive
|
|
||||||
- rowserrcheck
|
|
||||||
- stylecheck
|
|
||||||
- unconvert
|
|
||||||
- wastedassign
|
|
||||||
- whitespace
|
|
||||||
- wsl
|
|
||||||
30
vendor/github.com/eleksir/go-xmpp/LICENSE
generated
vendored
30
vendor/github.com/eleksir/go-xmpp/LICENSE
generated
vendored
@@ -1,30 +0,0 @@
|
|||||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
|
||||||
Copyright (c) 2012 Russ Cox <rsc@swtch.com>
|
|
||||||
Copyright (c) 2023 Yasuhiro Matsumoto <mattn.jp@gmail.com>
|
|
||||||
Copyright (c) 2023 Sergei Fedosoff <eleksir@gmail.com>
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are
|
|
||||||
met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following disclaimer
|
|
||||||
in the documentation and/or other materials provided with the
|
|
||||||
distribution.
|
|
||||||
* Neither the name of Google Inc. nor the names of its
|
|
||||||
contributors may be used to endorse or promote products derived from
|
|
||||||
this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
14
vendor/github.com/eleksir/go-xmpp/README.md
generated
vendored
14
vendor/github.com/eleksir/go-xmpp/README.md
generated
vendored
@@ -1,14 +0,0 @@
|
|||||||
go-xmpp
|
|
||||||
=======
|
|
||||||
|
|
||||||
go xmpp library.
|
|
||||||
|
|
||||||
[Original](https://github.com/rsc/rsc/blob/master/xmpp/xmpp.go) work done by [Russ Cox](https://github.com/rsc),
|
|
||||||
then [Yasuhiro Matsumoto](https://github.com/mattn) fork it and continue to improve it.
|
|
||||||
|
|
||||||
Since both of them have almost zero interest to continue develop this lib, I decide to fork it.
|
|
||||||
|
|
||||||
Documentation can be found at [godoc project](https://godoc.org/github.com/eleksir/go-xmpp)
|
|
||||||
|
|
||||||
This fork maintains my needs for xmpp protocol. Feel free to contribute via PR-s if you find it interesting too and feel
|
|
||||||
that it needs more love than it already has. And of course fell free to for it if needed.
|
|
||||||
1805
vendor/github.com/eleksir/go-xmpp/xmpp.go
generated
vendored
1805
vendor/github.com/eleksir/go-xmpp/xmpp.go
generated
vendored
File diff suppressed because it is too large
Load Diff
125
vendor/github.com/eleksir/go-xmpp/xmpp_information_query.go
generated
vendored
125
vendor/github.com/eleksir/go-xmpp/xmpp_information_query.go
generated
vendored
@@ -1,125 +0,0 @@
|
|||||||
package xmpp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
const IQTypeGet = "get"
|
|
||||||
const IQTypeSet = "set"
|
|
||||||
const IQTypeResult = "result"
|
|
||||||
const IQTypeError = "error"
|
|
||||||
|
|
||||||
func (c *Client) Discovery() (string, error) {
|
|
||||||
// use getCookie for a pseudo random id.
|
|
||||||
reqID := strconv.FormatUint(uint64(getCookie()), 10)
|
|
||||||
|
|
||||||
return c.RawInformationQuery(c.jid, c.domain, reqID, IQTypeGet, XMPPNS_DISCO_ITEMS, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// DiscoverNodeInfo discovers information about a node. Empty node queries info about server itself.
|
|
||||||
func (c *Client) DiscoverNodeInfo(node string) (string, error) {
|
|
||||||
query := fmt.Sprintf("<query xmlns='%s' node='%s'/>", XMPPNS_DISCO_INFO, node)
|
|
||||||
|
|
||||||
return c.RawInformation(c.jid, c.domain, "info3", IQTypeGet, query)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DiscoverInfo discovers information about given item from given jid.
|
|
||||||
func (c *Client) DiscoverInfo(from string, to string) (string, error) {
|
|
||||||
query := fmt.Sprintf("<query xmlns='%s'/>", XMPPNS_DISCO_INFO)
|
|
||||||
|
|
||||||
return c.RawInformation(from, to, "info3", IQTypeGet, query)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DiscoverServerItems discover items that the server exposes.
|
|
||||||
func (c *Client) DiscoverServerItems() (string, error) {
|
|
||||||
return c.DiscoverEntityItems(c.domain)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DiscoverEntityItems discovers items that an entity exposes.
|
|
||||||
func (c *Client) DiscoverEntityItems(jid string) (string, error) {
|
|
||||||
query := fmt.Sprintf("<query xmlns='%s'/>", XMPPNS_DISCO_ITEMS)
|
|
||||||
|
|
||||||
return c.RawInformation(c.jid, jid, "info1", IQTypeGet, query)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RawInformationQuery sends an information query request to the server.
|
|
||||||
func (c *Client) RawInformationQuery(from, to, id, iqType, requestNamespace, body string) (string, error) {
|
|
||||||
const xmlIQ = "<iq from='%s' to='%s' id='%s' type='%s'><query xmlns='%s'>%s</query></iq>"
|
|
||||||
_, err := fmt.Fprintf(StanzaWriter, xmlIQ, xmlEscape(from), xmlEscape(to), id, iqType, requestNamespace, body)
|
|
||||||
|
|
||||||
return id, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// RawInformation sends a IQ request with the payload body to the server.
|
|
||||||
func (c *Client) RawInformation(from, to, id, iqType, body string) (string, error) {
|
|
||||||
const xmlIQ = "<iq from='%s' to='%s' id='%s' type='%s'>%s</iq>"
|
|
||||||
_, err := fmt.Fprintf(StanzaWriter, xmlIQ, xmlEscape(from), xmlEscape(to), id, iqType, body)
|
|
||||||
|
|
||||||
return id, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// IqVersionResponse responding with software version, according to xep-0092.
|
|
||||||
func (c *Client) IqVersionResponse(v IQ, name, version, os string) (string, error) {
|
|
||||||
if name == "" {
|
|
||||||
name = "go-xmpp"
|
|
||||||
}
|
|
||||||
|
|
||||||
if version == "" {
|
|
||||||
version = "undefined"
|
|
||||||
}
|
|
||||||
|
|
||||||
query := "<query xmlns=\"jabber:iq:version\">"
|
|
||||||
query += fmt.Sprintf("<name>%s</name>", name)
|
|
||||||
query += fmt.Sprintf("<version>%s</version>", version)
|
|
||||||
|
|
||||||
if os != "" {
|
|
||||||
query += fmt.Sprintf("<os>%s</os>", os)
|
|
||||||
}
|
|
||||||
|
|
||||||
query += "</query>"
|
|
||||||
|
|
||||||
return c.RawInformation(
|
|
||||||
v.To,
|
|
||||||
v.From,
|
|
||||||
v.ID,
|
|
||||||
IQTypeResult,
|
|
||||||
query,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// JabberIqLastResponse responding with relative time since last activity.
|
|
||||||
// Here lastActivity is unix time stamp when last activity took place since.
|
|
||||||
func (c *Client) JabberIqLastResponse(v IQ, lastActivity int64) (string, error) {
|
|
||||||
query := fmt.Sprintf(
|
|
||||||
"<query xmlns=\"jabber:iq:last\" seconds=\"%d\" />",
|
|
||||||
time.Now().Unix()-lastActivity,
|
|
||||||
)
|
|
||||||
|
|
||||||
return c.RawInformation(
|
|
||||||
v.To,
|
|
||||||
v.From,
|
|
||||||
v.ID,
|
|
||||||
IQTypeResult,
|
|
||||||
query,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UrnXMPPTimeResponse implements response to query entity's current time (xep-0202).
|
|
||||||
// TimezoneOffset have format +HH:MM or -HH:MM.
|
|
||||||
func (c *Client) UrnXMPPTimeResponse(v IQ, timezoneOffset string) (string, error) {
|
|
||||||
query := fmt.Sprintf(
|
|
||||||
"<time xmlns=\"urn:xmpp:time\"><tzo>%s</tzo><utc>%s</utc></time>",
|
|
||||||
timezoneOffset,
|
|
||||||
time.Now().UTC().Format(time.RFC3339),
|
|
||||||
)
|
|
||||||
|
|
||||||
return c.RawInformation(
|
|
||||||
v.To,
|
|
||||||
v.From,
|
|
||||||
v.ID,
|
|
||||||
IQTypeResult,
|
|
||||||
query,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
140
vendor/github.com/eleksir/go-xmpp/xmpp_muc.go
generated
vendored
140
vendor/github.com/eleksir/go-xmpp/xmpp_muc.go
generated
vendored
@@ -1,140 +0,0 @@
|
|||||||
// Copyright 2013 Flo Lauber <dev@qatfy.at>. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// TODO(flo):
|
|
||||||
// - cleanup signatures of join/leave functions
|
|
||||||
package xmpp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
nsMUC = "http://jabber.org/protocol/muc"
|
|
||||||
nsMUCUser = "http://jabber.org/protocol/muc#user"
|
|
||||||
NoHistory = 0
|
|
||||||
CharHistory = 1
|
|
||||||
StanzaHistory = 2
|
|
||||||
SecondsHistory = 3
|
|
||||||
SinceHistory = 4
|
|
||||||
)
|
|
||||||
|
|
||||||
// SendTopic sends room topic wrapped inside an XMPP message stanza body.
|
|
||||||
func (c *Client) SendTopic(chat Chat) (int, error) {
|
|
||||||
return fmt.Fprintf(StanzaWriter, "<message to='%s' type='%s' xml:lang='en'>"+"<subject>%s</subject></message>",
|
|
||||||
xmlEscape(chat.Remote), xmlEscape(chat.Type), xmlEscape(chat.Text))
|
|
||||||
}
|
|
||||||
|
|
||||||
// JoinMUCNoHistory joins room and instructs server that no history data required.
|
|
||||||
func (c *Client) JoinMUCNoHistory(jid, nick string) (int, error) {
|
|
||||||
if nick == "" {
|
|
||||||
nick = c.jid
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Fprintf(StanzaWriter, "<presence to='%s/%s'>\n"+
|
|
||||||
"<x xmlns='%s'>"+
|
|
||||||
"<history maxchars='0'/></x>\n"+
|
|
||||||
"</presence>",
|
|
||||||
xmlEscape(jid), xmlEscape(nick), nsMUC)
|
|
||||||
}
|
|
||||||
|
|
||||||
// JoinMUC joins room, xep-0045 7.2.
|
|
||||||
func (c *Client) JoinMUC(jid, nick string, historyType, history int, historyDate *time.Time) (int, error) {
|
|
||||||
if nick == "" {
|
|
||||||
nick = c.jid
|
|
||||||
}
|
|
||||||
|
|
||||||
switch historyType {
|
|
||||||
case NoHistory:
|
|
||||||
return fmt.Fprintf(StanzaWriter, "<presence to='%s/%s'>\n"+
|
|
||||||
"<x xmlns='%s' />\n"+
|
|
||||||
"</presence>",
|
|
||||||
xmlEscape(jid), xmlEscape(nick), nsMUC)
|
|
||||||
case CharHistory:
|
|
||||||
return fmt.Fprintf(StanzaWriter, "<presence to='%s/%s'>\n"+
|
|
||||||
"<x xmlns='%s'>\n"+
|
|
||||||
"<history maxchars='%d'/></x>\n"+
|
|
||||||
"</presence>",
|
|
||||||
xmlEscape(jid), xmlEscape(nick), nsMUC, history)
|
|
||||||
case StanzaHistory:
|
|
||||||
return fmt.Fprintf(StanzaWriter, "<presence to='%s/%s'>\n"+
|
|
||||||
"<x xmlns='%s'>\n"+
|
|
||||||
"<history maxstanzas='%d'/></x>\n"+
|
|
||||||
"</presence>",
|
|
||||||
xmlEscape(jid), xmlEscape(nick), nsMUC, history)
|
|
||||||
case SecondsHistory:
|
|
||||||
return fmt.Fprintf(StanzaWriter, "<presence to='%s/%s'>\n"+
|
|
||||||
"<x xmlns='%s'>\n"+
|
|
||||||
"<history seconds='%d'/></x>\n"+
|
|
||||||
"</presence>",
|
|
||||||
xmlEscape(jid), xmlEscape(nick), nsMUC, history)
|
|
||||||
case SinceHistory:
|
|
||||||
if historyDate != nil {
|
|
||||||
return fmt.Fprintf(StanzaWriter, "<presence to='%s/%s'>\n"+
|
|
||||||
"<x xmlns='%s'>\n"+
|
|
||||||
"<history since='%s'/></x>\n"+
|
|
||||||
"</presence>",
|
|
||||||
xmlEscape(jid), xmlEscape(nick), nsMUC, historyDate.Format(time.RFC3339))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0, errors.New("unknown history option")
|
|
||||||
}
|
|
||||||
|
|
||||||
// JoinProtectedMUC joins password protected room, xep-0045 7.2.6.
|
|
||||||
func (c *Client) JoinProtectedMUC(jid, nick string, password string, historyType, history int, historyDate *time.Time) (int, error) {
|
|
||||||
if nick == "" {
|
|
||||||
nick = c.jid
|
|
||||||
}
|
|
||||||
|
|
||||||
switch historyType {
|
|
||||||
case NoHistory:
|
|
||||||
return fmt.Fprintf(StanzaWriter, "<presence to='%s/%s'>\n"+
|
|
||||||
"<x xmlns='%s'>\n"+
|
|
||||||
"<password>%s</password>"+
|
|
||||||
"</x>\n"+
|
|
||||||
"</presence>",
|
|
||||||
xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password))
|
|
||||||
case CharHistory:
|
|
||||||
return fmt.Fprintf(StanzaWriter, "<presence to='%s/%s'>\n"+
|
|
||||||
"<x xmlns='%s'>\n"+
|
|
||||||
"<password>%s</password>\n"+
|
|
||||||
"<history maxchars='%d'/></x>\n"+
|
|
||||||
"</presence>",
|
|
||||||
xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history)
|
|
||||||
case StanzaHistory:
|
|
||||||
return fmt.Fprintf(StanzaWriter, "<presence to='%s/%s'>\n"+
|
|
||||||
"<x xmlns='%s'>\n"+
|
|
||||||
"<password>%s</password>\n"+
|
|
||||||
"<history maxstanzas='%d'/></x>\n"+
|
|
||||||
"</presence>",
|
|
||||||
xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history)
|
|
||||||
case SecondsHistory:
|
|
||||||
return fmt.Fprintf(StanzaWriter, "<presence to='%s/%s'>\n"+
|
|
||||||
"<x xmlns='%s'>\n"+
|
|
||||||
"<password>%s</password>\n"+
|
|
||||||
"<history seconds='%d'/></x>\n"+
|
|
||||||
"</presence>",
|
|
||||||
xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history)
|
|
||||||
case SinceHistory:
|
|
||||||
if historyDate != nil {
|
|
||||||
return fmt.Fprintf(StanzaWriter, "<presence to='%s/%s'>\n"+
|
|
||||||
"<x xmlns='%s'>\n"+
|
|
||||||
"<password>%s</password>\n"+
|
|
||||||
"<history since='%s'/></x>\n"+
|
|
||||||
"</presence>",
|
|
||||||
xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), historyDate.Format(time.RFC3339))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0, errors.New("unknown history option")
|
|
||||||
}
|
|
||||||
|
|
||||||
// LeaveMUC quits from room xep-0045 7.14.
|
|
||||||
func (c *Client) LeaveMUC(jid string) (int, error) {
|
|
||||||
return fmt.Fprintf(StanzaWriter, "<presence from='%s' to='%s' type='unavailable' />",
|
|
||||||
c.jid, xmlEscape(jid))
|
|
||||||
}
|
|
||||||
49
vendor/github.com/eleksir/go-xmpp/xmpp_ping.go
generated
vendored
49
vendor/github.com/eleksir/go-xmpp/xmpp_ping.go
generated
vendored
@@ -1,49 +0,0 @@
|
|||||||
package xmpp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *Client) PingC2S(jid, server string) error {
|
|
||||||
if jid == "" {
|
|
||||||
jid = c.jid
|
|
||||||
}
|
|
||||||
|
|
||||||
if server == "" {
|
|
||||||
server = c.domain
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := fmt.Fprintf(StanzaWriter, "<iq from='%s' to='%s' id='c2s1' type='get'>\n"+
|
|
||||||
"<ping xmlns='urn:xmpp:ping'/>\n"+
|
|
||||||
"</iq>",
|
|
||||||
xmlEscape(jid), xmlEscape(server))
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) PingS2S(fromServer, toServer string) error {
|
|
||||||
_, err := fmt.Fprintf(StanzaWriter, "<iq from='%s' to='%s' id='s2s1' type='get'>\n"+
|
|
||||||
"<ping xmlns='urn:xmpp:ping'/>\n"+
|
|
||||||
"</iq>",
|
|
||||||
xmlEscape(fromServer), xmlEscape(toServer))
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) SendResultPing(id, toServer string) error {
|
|
||||||
_, err := fmt.Fprintf(StanzaWriter, "<iq type='result' to='%s' id='%s'/>",
|
|
||||||
xmlEscape(toServer), xmlEscape(id))
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// PingResponse responding to ping query according to xep-0199.
|
|
||||||
func (c *Client) PingResponse(v IQ) (string, error) {
|
|
||||||
return c.RawInformation(
|
|
||||||
v.To,
|
|
||||||
v.From,
|
|
||||||
v.ID,
|
|
||||||
IQTypeResult,
|
|
||||||
"",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
33
vendor/github.com/eleksir/go-xmpp/xmpp_subscription.go
generated
vendored
33
vendor/github.com/eleksir/go-xmpp/xmpp_subscription.go
generated
vendored
@@ -1,33 +0,0 @@
|
|||||||
package xmpp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *Client) ApproveSubscription(jid string) error {
|
|
||||||
_, err := fmt.Fprintf(StanzaWriter, "<presence to='%s' type='subscribed'/>",
|
|
||||||
xmlEscape(jid))
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) RevokeSubscription(jid string) error {
|
|
||||||
_, err := fmt.Fprintf(StanzaWriter, "<presence to='%s' type='unsubscribed'/>",
|
|
||||||
xmlEscape(jid))
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) RetrieveSubscription(jid string) error {
|
|
||||||
_, err := fmt.Fprintf(c.conn, "<presence to='%s' type='unsubscribe'/>",
|
|
||||||
xmlEscape(jid))
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Client) RequestSubscription(jid string) error {
|
|
||||||
_, err := fmt.Fprintf(StanzaWriter, "<presence to='%s' type='subscribe'/>",
|
|
||||||
xmlEscape(jid))
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
4
vendor/github.com/sirupsen/logrus/.gitignore
generated
vendored
4
vendor/github.com/sirupsen/logrus/.gitignore
generated
vendored
@@ -1,4 +0,0 @@
|
|||||||
logrus
|
|
||||||
vendor
|
|
||||||
|
|
||||||
.idea/
|
|
||||||
40
vendor/github.com/sirupsen/logrus/.golangci.yml
generated
vendored
40
vendor/github.com/sirupsen/logrus/.golangci.yml
generated
vendored
@@ -1,40 +0,0 @@
|
|||||||
run:
|
|
||||||
# do not run on test files yet
|
|
||||||
tests: false
|
|
||||||
|
|
||||||
# all available settings of specific linters
|
|
||||||
linters-settings:
|
|
||||||
errcheck:
|
|
||||||
# report about not checking of errors in type assetions: `a := b.(MyStruct)`;
|
|
||||||
# default is false: such cases aren't reported by default.
|
|
||||||
check-type-assertions: false
|
|
||||||
|
|
||||||
# report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`;
|
|
||||||
# default is false: such cases aren't reported by default.
|
|
||||||
check-blank: false
|
|
||||||
|
|
||||||
lll:
|
|
||||||
line-length: 100
|
|
||||||
tab-width: 4
|
|
||||||
|
|
||||||
prealloc:
|
|
||||||
simple: false
|
|
||||||
range-loops: false
|
|
||||||
for-loops: false
|
|
||||||
|
|
||||||
whitespace:
|
|
||||||
multi-if: false # Enforces newlines (or comments) after every multi-line if statement
|
|
||||||
multi-func: false # Enforces newlines (or comments) after every multi-line function signature
|
|
||||||
|
|
||||||
linters:
|
|
||||||
enable:
|
|
||||||
- megacheck
|
|
||||||
- govet
|
|
||||||
disable:
|
|
||||||
- maligned
|
|
||||||
- prealloc
|
|
||||||
disable-all: false
|
|
||||||
presets:
|
|
||||||
- bugs
|
|
||||||
- unused
|
|
||||||
fast: false
|
|
||||||
15
vendor/github.com/sirupsen/logrus/.travis.yml
generated
vendored
15
vendor/github.com/sirupsen/logrus/.travis.yml
generated
vendored
@@ -1,15 +0,0 @@
|
|||||||
language: go
|
|
||||||
go_import_path: github.com/sirupsen/logrus
|
|
||||||
git:
|
|
||||||
depth: 1
|
|
||||||
env:
|
|
||||||
- GO111MODULE=on
|
|
||||||
go: 1.15.x
|
|
||||||
os: linux
|
|
||||||
install:
|
|
||||||
- ./travis/install.sh
|
|
||||||
script:
|
|
||||||
- cd ci
|
|
||||||
- go run mage.go -v -w ../ crossBuild
|
|
||||||
- go run mage.go -v -w ../ lint
|
|
||||||
- go run mage.go -v -w ../ test
|
|
||||||
259
vendor/github.com/sirupsen/logrus/CHANGELOG.md
generated
vendored
259
vendor/github.com/sirupsen/logrus/CHANGELOG.md
generated
vendored
@@ -1,259 +0,0 @@
|
|||||||
# 1.8.1
|
|
||||||
Code quality:
|
|
||||||
* move magefile in its own subdir/submodule to remove magefile dependency on logrus consumer
|
|
||||||
* improve timestamp format documentation
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
* fix race condition on logger hooks
|
|
||||||
|
|
||||||
|
|
||||||
# 1.8.0
|
|
||||||
|
|
||||||
Correct versioning number replacing v1.7.1.
|
|
||||||
|
|
||||||
# 1.7.1
|
|
||||||
|
|
||||||
Beware this release has introduced a new public API and its semver is therefore incorrect.
|
|
||||||
|
|
||||||
Code quality:
|
|
||||||
* use go 1.15 in travis
|
|
||||||
* use magefile as task runner
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
* small fixes about new go 1.13 error formatting system
|
|
||||||
* Fix for long time race condiction with mutating data hooks
|
|
||||||
|
|
||||||
Features:
|
|
||||||
* build support for zos
|
|
||||||
|
|
||||||
# 1.7.0
|
|
||||||
Fixes:
|
|
||||||
* the dependency toward a windows terminal library has been removed
|
|
||||||
|
|
||||||
Features:
|
|
||||||
* a new buffer pool management API has been added
|
|
||||||
* a set of `<LogLevel>Fn()` functions have been added
|
|
||||||
|
|
||||||
# 1.6.0
|
|
||||||
Fixes:
|
|
||||||
* end of line cleanup
|
|
||||||
* revert the entry concurrency bug fix whic leads to deadlock under some circumstances
|
|
||||||
* update dependency on go-windows-terminal-sequences to fix a crash with go 1.14
|
|
||||||
|
|
||||||
Features:
|
|
||||||
* add an option to the `TextFormatter` to completely disable fields quoting
|
|
||||||
|
|
||||||
# 1.5.0
|
|
||||||
Code quality:
|
|
||||||
* add golangci linter run on travis
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
* add mutex for hooks concurrent access on `Entry` data
|
|
||||||
* caller function field for go1.14
|
|
||||||
* fix build issue for gopherjs target
|
|
||||||
|
|
||||||
Feature:
|
|
||||||
* add an hooks/writer sub-package whose goal is to split output on different stream depending on the trace level
|
|
||||||
* add a `DisableHTMLEscape` option in the `JSONFormatter`
|
|
||||||
* add `ForceQuote` and `PadLevelText` options in the `TextFormatter`
|
|
||||||
|
|
||||||
# 1.4.2
|
|
||||||
* Fixes build break for plan9, nacl, solaris
|
|
||||||
# 1.4.1
|
|
||||||
This new release introduces:
|
|
||||||
* Enhance TextFormatter to not print caller information when they are empty (#944)
|
|
||||||
* Remove dependency on golang.org/x/crypto (#932, #943)
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
* Fix Entry.WithContext method to return a copy of the initial entry (#941)
|
|
||||||
|
|
||||||
# 1.4.0
|
|
||||||
This new release introduces:
|
|
||||||
* Add `DeferExitHandler`, similar to `RegisterExitHandler` but prepending the handler to the list of handlers (semantically like `defer`) (#848).
|
|
||||||
* Add `CallerPrettyfier` to `JSONFormatter` and `TextFormatter` (#909, #911)
|
|
||||||
* Add `Entry.WithContext()` and `Entry.Context`, to set a context on entries to be used e.g. in hooks (#919).
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
* Fix wrong method calls `Logger.Print` and `Logger.Warningln` (#893).
|
|
||||||
* Update `Entry.Logf` to not do string formatting unless the log level is enabled (#903)
|
|
||||||
* Fix infinite recursion on unknown `Level.String()` (#907)
|
|
||||||
* Fix race condition in `getCaller` (#916).
|
|
||||||
|
|
||||||
|
|
||||||
# 1.3.0
|
|
||||||
This new release introduces:
|
|
||||||
* Log, Logf, Logln functions for Logger and Entry that take a Level
|
|
||||||
|
|
||||||
Fixes:
|
|
||||||
* Building prometheus node_exporter on AIX (#840)
|
|
||||||
* Race condition in TextFormatter (#468)
|
|
||||||
* Travis CI import path (#868)
|
|
||||||
* Remove coloured output on Windows (#862)
|
|
||||||
* Pointer to func as field in JSONFormatter (#870)
|
|
||||||
* Properly marshal Levels (#873)
|
|
||||||
|
|
||||||
# 1.2.0
|
|
||||||
This new release introduces:
|
|
||||||
* A new method `SetReportCaller` in the `Logger` to enable the file, line and calling function from which the trace has been issued
|
|
||||||
* A new trace level named `Trace` whose level is below `Debug`
|
|
||||||
* A configurable exit function to be called upon a Fatal trace
|
|
||||||
* The `Level` object now implements `encoding.TextUnmarshaler` interface
|
|
||||||
|
|
||||||
# 1.1.1
|
|
||||||
This is a bug fix release.
|
|
||||||
* fix the build break on Solaris
|
|
||||||
* don't drop a whole trace in JSONFormatter when a field param is a function pointer which can not be serialized
|
|
||||||
|
|
||||||
# 1.1.0
|
|
||||||
This new release introduces:
|
|
||||||
* several fixes:
|
|
||||||
* a fix for a race condition on entry formatting
|
|
||||||
* proper cleanup of previously used entries before putting them back in the pool
|
|
||||||
* the extra new line at the end of message in text formatter has been removed
|
|
||||||
* a new global public API to check if a level is activated: IsLevelEnabled
|
|
||||||
* the following methods have been added to the Logger object
|
|
||||||
* IsLevelEnabled
|
|
||||||
* SetFormatter
|
|
||||||
* SetOutput
|
|
||||||
* ReplaceHooks
|
|
||||||
* introduction of go module
|
|
||||||
* an indent configuration for the json formatter
|
|
||||||
* output colour support for windows
|
|
||||||
* the field sort function is now configurable for text formatter
|
|
||||||
* the CLICOLOR and CLICOLOR\_FORCE environment variable support in text formater
|
|
||||||
|
|
||||||
# 1.0.6
|
|
||||||
|
|
||||||
This new release introduces:
|
|
||||||
* a new api WithTime which allows to easily force the time of the log entry
|
|
||||||
which is mostly useful for logger wrapper
|
|
||||||
* a fix reverting the immutability of the entry given as parameter to the hooks
|
|
||||||
a new configuration field of the json formatter in order to put all the fields
|
|
||||||
in a nested dictionnary
|
|
||||||
* a new SetOutput method in the Logger
|
|
||||||
* a new configuration of the textformatter to configure the name of the default keys
|
|
||||||
* a new configuration of the text formatter to disable the level truncation
|
|
||||||
|
|
||||||
# 1.0.5
|
|
||||||
|
|
||||||
* Fix hooks race (#707)
|
|
||||||
* Fix panic deadlock (#695)
|
|
||||||
|
|
||||||
# 1.0.4
|
|
||||||
|
|
||||||
* Fix race when adding hooks (#612)
|
|
||||||
* Fix terminal check in AppEngine (#635)
|
|
||||||
|
|
||||||
# 1.0.3
|
|
||||||
|
|
||||||
* Replace example files with testable examples
|
|
||||||
|
|
||||||
# 1.0.2
|
|
||||||
|
|
||||||
* bug: quote non-string values in text formatter (#583)
|
|
||||||
* Make (*Logger) SetLevel a public method
|
|
||||||
|
|
||||||
# 1.0.1
|
|
||||||
|
|
||||||
* bug: fix escaping in text formatter (#575)
|
|
||||||
|
|
||||||
# 1.0.0
|
|
||||||
|
|
||||||
* Officially changed name to lower-case
|
|
||||||
* bug: colors on Windows 10 (#541)
|
|
||||||
* bug: fix race in accessing level (#512)
|
|
||||||
|
|
||||||
# 0.11.5
|
|
||||||
|
|
||||||
* feature: add writer and writerlevel to entry (#372)
|
|
||||||
|
|
||||||
# 0.11.4
|
|
||||||
|
|
||||||
* bug: fix undefined variable on solaris (#493)
|
|
||||||
|
|
||||||
# 0.11.3
|
|
||||||
|
|
||||||
* formatter: configure quoting of empty values (#484)
|
|
||||||
* formatter: configure quoting character (default is `"`) (#484)
|
|
||||||
* bug: fix not importing io correctly in non-linux environments (#481)
|
|
||||||
|
|
||||||
# 0.11.2
|
|
||||||
|
|
||||||
* bug: fix windows terminal detection (#476)
|
|
||||||
|
|
||||||
# 0.11.1
|
|
||||||
|
|
||||||
* bug: fix tty detection with custom out (#471)
|
|
||||||
|
|
||||||
# 0.11.0
|
|
||||||
|
|
||||||
* performance: Use bufferpool to allocate (#370)
|
|
||||||
* terminal: terminal detection for app-engine (#343)
|
|
||||||
* feature: exit handler (#375)
|
|
||||||
|
|
||||||
# 0.10.0
|
|
||||||
|
|
||||||
* feature: Add a test hook (#180)
|
|
||||||
* feature: `ParseLevel` is now case-insensitive (#326)
|
|
||||||
* feature: `FieldLogger` interface that generalizes `Logger` and `Entry` (#308)
|
|
||||||
* performance: avoid re-allocations on `WithFields` (#335)
|
|
||||||
|
|
||||||
# 0.9.0
|
|
||||||
|
|
||||||
* logrus/text_formatter: don't emit empty msg
|
|
||||||
* logrus/hooks/airbrake: move out of main repository
|
|
||||||
* logrus/hooks/sentry: move out of main repository
|
|
||||||
* logrus/hooks/papertrail: move out of main repository
|
|
||||||
* logrus/hooks/bugsnag: move out of main repository
|
|
||||||
* logrus/core: run tests with `-race`
|
|
||||||
* logrus/core: detect TTY based on `stderr`
|
|
||||||
* logrus/core: support `WithError` on logger
|
|
||||||
* logrus/core: Solaris support
|
|
||||||
|
|
||||||
# 0.8.7
|
|
||||||
|
|
||||||
* logrus/core: fix possible race (#216)
|
|
||||||
* logrus/doc: small typo fixes and doc improvements
|
|
||||||
|
|
||||||
|
|
||||||
# 0.8.6
|
|
||||||
|
|
||||||
* hooks/raven: allow passing an initialized client
|
|
||||||
|
|
||||||
# 0.8.5
|
|
||||||
|
|
||||||
* logrus/core: revert #208
|
|
||||||
|
|
||||||
# 0.8.4
|
|
||||||
|
|
||||||
* formatter/text: fix data race (#218)
|
|
||||||
|
|
||||||
# 0.8.3
|
|
||||||
|
|
||||||
* logrus/core: fix entry log level (#208)
|
|
||||||
* logrus/core: improve performance of text formatter by 40%
|
|
||||||
* logrus/core: expose `LevelHooks` type
|
|
||||||
* logrus/core: add support for DragonflyBSD and NetBSD
|
|
||||||
* formatter/text: print structs more verbosely
|
|
||||||
|
|
||||||
# 0.8.2
|
|
||||||
|
|
||||||
* logrus: fix more Fatal family functions
|
|
||||||
|
|
||||||
# 0.8.1
|
|
||||||
|
|
||||||
* logrus: fix not exiting on `Fatalf` and `Fatalln`
|
|
||||||
|
|
||||||
# 0.8.0
|
|
||||||
|
|
||||||
* logrus: defaults to stderr instead of stdout
|
|
||||||
* hooks/sentry: add special field for `*http.Request`
|
|
||||||
* formatter/text: ignore Windows for colors
|
|
||||||
|
|
||||||
# 0.7.3
|
|
||||||
|
|
||||||
* formatter/\*: allow configuration of timestamp layout
|
|
||||||
|
|
||||||
# 0.7.2
|
|
||||||
|
|
||||||
* formatter/text: Add configuration option for time format (#158)
|
|
||||||
21
vendor/github.com/sirupsen/logrus/LICENSE
generated
vendored
21
vendor/github.com/sirupsen/logrus/LICENSE
generated
vendored
@@ -1,21 +0,0 @@
|
|||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2014 Simon Eskildsen
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
515
vendor/github.com/sirupsen/logrus/README.md
generated
vendored
515
vendor/github.com/sirupsen/logrus/README.md
generated
vendored
@@ -1,515 +0,0 @@
|
|||||||
# Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/> [](https://github.com/sirupsen/logrus/actions?query=workflow%3ACI) [](https://travis-ci.org/sirupsen/logrus) [](https://pkg.go.dev/github.com/sirupsen/logrus)
|
|
||||||
|
|
||||||
Logrus is a structured logger for Go (golang), completely API compatible with
|
|
||||||
the standard library logger.
|
|
||||||
|
|
||||||
**Logrus is in maintenance-mode.** We will not be introducing new features. It's
|
|
||||||
simply too hard to do in a way that won't break many people's projects, which is
|
|
||||||
the last thing you want from your Logging library (again...).
|
|
||||||
|
|
||||||
This does not mean Logrus is dead. Logrus will continue to be maintained for
|
|
||||||
security, (backwards compatible) bug fixes, and performance (where we are
|
|
||||||
limited by the interface).
|
|
||||||
|
|
||||||
I believe Logrus' biggest contribution is to have played a part in today's
|
|
||||||
widespread use of structured logging in Golang. There doesn't seem to be a
|
|
||||||
reason to do a major, breaking iteration into Logrus V2, since the fantastic Go
|
|
||||||
community has built those independently. Many fantastic alternatives have sprung
|
|
||||||
up. Logrus would look like those, had it been re-designed with what we know
|
|
||||||
about structured logging in Go today. Check out, for example,
|
|
||||||
[Zerolog][zerolog], [Zap][zap], and [Apex][apex].
|
|
||||||
|
|
||||||
[zerolog]: https://github.com/rs/zerolog
|
|
||||||
[zap]: https://github.com/uber-go/zap
|
|
||||||
[apex]: https://github.com/apex/log
|
|
||||||
|
|
||||||
**Seeing weird case-sensitive problems?** It's in the past been possible to
|
|
||||||
import Logrus as both upper- and lower-case. Due to the Go package environment,
|
|
||||||
this caused issues in the community and we needed a standard. Some environments
|
|
||||||
experienced problems with the upper-case variant, so the lower-case was decided.
|
|
||||||
Everything using `logrus` will need to use the lower-case:
|
|
||||||
`github.com/sirupsen/logrus`. Any package that isn't, should be changed.
|
|
||||||
|
|
||||||
To fix Glide, see [these
|
|
||||||
comments](https://github.com/sirupsen/logrus/issues/553#issuecomment-306591437).
|
|
||||||
For an in-depth explanation of the casing issue, see [this
|
|
||||||
comment](https://github.com/sirupsen/logrus/issues/570#issuecomment-313933276).
|
|
||||||
|
|
||||||
Nicely color-coded in development (when a TTY is attached, otherwise just
|
|
||||||
plain text):
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
With `log.SetFormatter(&log.JSONFormatter{})`, for easy parsing by logstash
|
|
||||||
or Splunk:
|
|
||||||
|
|
||||||
```text
|
|
||||||
{"animal":"walrus","level":"info","msg":"A group of walrus emerges from the
|
|
||||||
ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"}
|
|
||||||
|
|
||||||
{"level":"warning","msg":"The group's number increased tremendously!",
|
|
||||||
"number":122,"omg":true,"time":"2014-03-10 19:57:38.562471297 -0400 EDT"}
|
|
||||||
|
|
||||||
{"animal":"walrus","level":"info","msg":"A giant walrus appears!",
|
|
||||||
"size":10,"time":"2014-03-10 19:57:38.562500591 -0400 EDT"}
|
|
||||||
|
|
||||||
{"animal":"walrus","level":"info","msg":"Tremendously sized cow enters the ocean.",
|
|
||||||
"size":9,"time":"2014-03-10 19:57:38.562527896 -0400 EDT"}
|
|
||||||
|
|
||||||
{"level":"fatal","msg":"The ice breaks!","number":100,"omg":true,
|
|
||||||
"time":"2014-03-10 19:57:38.562543128 -0400 EDT"}
|
|
||||||
```
|
|
||||||
|
|
||||||
With the default `log.SetFormatter(&log.TextFormatter{})` when a TTY is not
|
|
||||||
attached, the output is compatible with the
|
|
||||||
[logfmt](http://godoc.org/github.com/kr/logfmt) format:
|
|
||||||
|
|
||||||
```text
|
|
||||||
time="2015-03-26T01:27:38-04:00" level=debug msg="Started observing beach" animal=walrus number=8
|
|
||||||
time="2015-03-26T01:27:38-04:00" level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10
|
|
||||||
time="2015-03-26T01:27:38-04:00" level=warning msg="The group's number increased tremendously!" number=122 omg=true
|
|
||||||
time="2015-03-26T01:27:38-04:00" level=debug msg="Temperature changes" temperature=-4
|
|
||||||
time="2015-03-26T01:27:38-04:00" level=panic msg="It's over 9000!" animal=orca size=9009
|
|
||||||
time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" err=&{0x2082280c0 map[animal:orca size:9009] 2015-03-26 01:27:38.441574009 -0400 EDT panic It's over 9000!} number=100 omg=true
|
|
||||||
```
|
|
||||||
To ensure this behaviour even if a TTY is attached, set your formatter as follows:
|
|
||||||
|
|
||||||
```go
|
|
||||||
log.SetFormatter(&log.TextFormatter{
|
|
||||||
DisableColors: true,
|
|
||||||
FullTimestamp: true,
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Logging Method Name
|
|
||||||
|
|
||||||
If you wish to add the calling method as a field, instruct the logger via:
|
|
||||||
```go
|
|
||||||
log.SetReportCaller(true)
|
|
||||||
```
|
|
||||||
This adds the caller as 'method' like so:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{"animal":"penguin","level":"fatal","method":"github.com/sirupsen/arcticcreatures.migrate","msg":"a penguin swims by",
|
|
||||||
"time":"2014-03-10 19:57:38.562543129 -0400 EDT"}
|
|
||||||
```
|
|
||||||
|
|
||||||
```text
|
|
||||||
time="2015-03-26T01:27:38-04:00" level=fatal method=github.com/sirupsen/arcticcreatures.migrate msg="a penguin swims by" animal=penguin
|
|
||||||
```
|
|
||||||
Note that this does add measurable overhead - the cost will depend on the version of Go, but is
|
|
||||||
between 20 and 40% in recent tests with 1.6 and 1.7. You can validate this in your
|
|
||||||
environment via benchmarks:
|
|
||||||
```
|
|
||||||
go test -bench=.*CallerTracing
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
#### Case-sensitivity
|
|
||||||
|
|
||||||
The organization's name was changed to lower-case--and this will not be changed
|
|
||||||
back. If you are getting import conflicts due to case sensitivity, please use
|
|
||||||
the lower-case import: `github.com/sirupsen/logrus`.
|
|
||||||
|
|
||||||
#### Example
|
|
||||||
|
|
||||||
The simplest way to use Logrus is simply the package-level exported logger:
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
log.WithFields(log.Fields{
|
|
||||||
"animal": "walrus",
|
|
||||||
}).Info("A walrus appears")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that it's completely api-compatible with the stdlib logger, so you can
|
|
||||||
replace your `log` imports everywhere with `log "github.com/sirupsen/logrus"`
|
|
||||||
and you'll now have the flexibility of Logrus. You can customize it all you
|
|
||||||
want:
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
// Log as JSON instead of the default ASCII formatter.
|
|
||||||
log.SetFormatter(&log.JSONFormatter{})
|
|
||||||
|
|
||||||
// Output to stdout instead of the default stderr
|
|
||||||
// Can be any io.Writer, see below for File example
|
|
||||||
log.SetOutput(os.Stdout)
|
|
||||||
|
|
||||||
// Only log the warning severity or above.
|
|
||||||
log.SetLevel(log.WarnLevel)
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
log.WithFields(log.Fields{
|
|
||||||
"animal": "walrus",
|
|
||||||
"size": 10,
|
|
||||||
}).Info("A group of walrus emerges from the ocean")
|
|
||||||
|
|
||||||
log.WithFields(log.Fields{
|
|
||||||
"omg": true,
|
|
||||||
"number": 122,
|
|
||||||
}).Warn("The group's number increased tremendously!")
|
|
||||||
|
|
||||||
log.WithFields(log.Fields{
|
|
||||||
"omg": true,
|
|
||||||
"number": 100,
|
|
||||||
}).Fatal("The ice breaks!")
|
|
||||||
|
|
||||||
// A common pattern is to re-use fields between logging statements by re-using
|
|
||||||
// the logrus.Entry returned from WithFields()
|
|
||||||
contextLogger := log.WithFields(log.Fields{
|
|
||||||
"common": "this is a common field",
|
|
||||||
"other": "I also should be logged always",
|
|
||||||
})
|
|
||||||
|
|
||||||
contextLogger.Info("I'll be logged with common and other field")
|
|
||||||
contextLogger.Info("Me too")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
For more advanced usage such as logging to multiple locations from the same
|
|
||||||
application, you can also create an instance of the `logrus` Logger:
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Create a new instance of the logger. You can have any number of instances.
|
|
||||||
var log = logrus.New()
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
// The API for setting attributes is a little different than the package level
|
|
||||||
// exported logger. See Godoc.
|
|
||||||
log.Out = os.Stdout
|
|
||||||
|
|
||||||
// You could set this to any `io.Writer` such as a file
|
|
||||||
// file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
|
||||||
// if err == nil {
|
|
||||||
// log.Out = file
|
|
||||||
// } else {
|
|
||||||
// log.Info("Failed to log to file, using default stderr")
|
|
||||||
// }
|
|
||||||
|
|
||||||
log.WithFields(logrus.Fields{
|
|
||||||
"animal": "walrus",
|
|
||||||
"size": 10,
|
|
||||||
}).Info("A group of walrus emerges from the ocean")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Fields
|
|
||||||
|
|
||||||
Logrus encourages careful, structured logging through logging fields instead of
|
|
||||||
long, unparseable error messages. For example, instead of: `log.Fatalf("Failed
|
|
||||||
to send event %s to topic %s with key %d")`, you should log the much more
|
|
||||||
discoverable:
|
|
||||||
|
|
||||||
```go
|
|
||||||
log.WithFields(log.Fields{
|
|
||||||
"event": event,
|
|
||||||
"topic": topic,
|
|
||||||
"key": key,
|
|
||||||
}).Fatal("Failed to send event")
|
|
||||||
```
|
|
||||||
|
|
||||||
We've found this API forces you to think about logging in a way that produces
|
|
||||||
much more useful logging messages. We've been in countless situations where just
|
|
||||||
a single added field to a log statement that was already there would've saved us
|
|
||||||
hours. The `WithFields` call is optional.
|
|
||||||
|
|
||||||
In general, with Logrus using any of the `printf`-family functions should be
|
|
||||||
seen as a hint you should add a field, however, you can still use the
|
|
||||||
`printf`-family functions with Logrus.
|
|
||||||
|
|
||||||
#### Default Fields
|
|
||||||
|
|
||||||
Often it's helpful to have fields _always_ attached to log statements in an
|
|
||||||
application or parts of one. For example, you may want to always log the
|
|
||||||
`request_id` and `user_ip` in the context of a request. Instead of writing
|
|
||||||
`log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip})` on
|
|
||||||
every line, you can create a `logrus.Entry` to pass around instead:
|
|
||||||
|
|
||||||
```go
|
|
||||||
requestLogger := log.WithFields(log.Fields{"request_id": request_id, "user_ip": user_ip})
|
|
||||||
requestLogger.Info("something happened on that request") # will log request_id and user_ip
|
|
||||||
requestLogger.Warn("something not great happened")
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Hooks
|
|
||||||
|
|
||||||
You can add hooks for logging levels. For example to send errors to an exception
|
|
||||||
tracking service on `Error`, `Fatal` and `Panic`, info to StatsD or log to
|
|
||||||
multiple places simultaneously, e.g. syslog.
|
|
||||||
|
|
||||||
Logrus comes with [built-in hooks](hooks/). Add those, or your custom hook, in
|
|
||||||
`init`:
|
|
||||||
|
|
||||||
```go
|
|
||||||
import (
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"gopkg.in/gemnasium/logrus-airbrake-hook.v2" // the package is named "airbrake"
|
|
||||||
logrus_syslog "github.com/sirupsen/logrus/hooks/syslog"
|
|
||||||
"log/syslog"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
|
|
||||||
// Use the Airbrake hook to report errors that have Error severity or above to
|
|
||||||
// an exception tracker. You can create custom hooks, see the Hooks section.
|
|
||||||
log.AddHook(airbrake.NewHook(123, "xyz", "production"))
|
|
||||||
|
|
||||||
hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Unable to connect to local syslog daemon")
|
|
||||||
} else {
|
|
||||||
log.AddHook(hook)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). For the detail, please check the [syslog hook README](hooks/syslog/README.md).
|
|
||||||
|
|
||||||
A list of currently known service hooks can be found in this wiki [page](https://github.com/sirupsen/logrus/wiki/Hooks)
|
|
||||||
|
|
||||||
|
|
||||||
#### Level logging
|
|
||||||
|
|
||||||
Logrus has seven logging levels: Trace, Debug, Info, Warning, Error, Fatal and Panic.
|
|
||||||
|
|
||||||
```go
|
|
||||||
log.Trace("Something very low level.")
|
|
||||||
log.Debug("Useful debugging information.")
|
|
||||||
log.Info("Something noteworthy happened!")
|
|
||||||
log.Warn("You should probably take a look at this.")
|
|
||||||
log.Error("Something failed but I'm not quitting.")
|
|
||||||
// Calls os.Exit(1) after logging
|
|
||||||
log.Fatal("Bye.")
|
|
||||||
// Calls panic() after logging
|
|
||||||
log.Panic("I'm bailing.")
|
|
||||||
```
|
|
||||||
|
|
||||||
You can set the logging level on a `Logger`, then it will only log entries with
|
|
||||||
that severity or anything above it:
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Will log anything that is info or above (warn, error, fatal, panic). Default.
|
|
||||||
log.SetLevel(log.InfoLevel)
|
|
||||||
```
|
|
||||||
|
|
||||||
It may be useful to set `log.Level = logrus.DebugLevel` in a debug or verbose
|
|
||||||
environment if your application has that.
|
|
||||||
|
|
||||||
Note: If you want different log levels for global (`log.SetLevel(...)`) and syslog logging, please check the [syslog hook README](hooks/syslog/README.md#different-log-levels-for-local-and-remote-logging).
|
|
||||||
|
|
||||||
#### Entries
|
|
||||||
|
|
||||||
Besides the fields added with `WithField` or `WithFields` some fields are
|
|
||||||
automatically added to all logging events:
|
|
||||||
|
|
||||||
1. `time`. The timestamp when the entry was created.
|
|
||||||
2. `msg`. The logging message passed to `{Info,Warn,Error,Fatal,Panic}` after
|
|
||||||
the `AddFields` call. E.g. `Failed to send event.`
|
|
||||||
3. `level`. The logging level. E.g. `info`.
|
|
||||||
|
|
||||||
#### Environments
|
|
||||||
|
|
||||||
Logrus has no notion of environment.
|
|
||||||
|
|
||||||
If you wish for hooks and formatters to only be used in specific environments,
|
|
||||||
you should handle that yourself. For example, if your application has a global
|
|
||||||
variable `Environment`, which is a string representation of the environment you
|
|
||||||
could do:
|
|
||||||
|
|
||||||
```go
|
|
||||||
import (
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
// do something here to set environment depending on an environment variable
|
|
||||||
// or command-line flag
|
|
||||||
if Environment == "production" {
|
|
||||||
log.SetFormatter(&log.JSONFormatter{})
|
|
||||||
} else {
|
|
||||||
// The TextFormatter is default, you don't actually have to do this.
|
|
||||||
log.SetFormatter(&log.TextFormatter{})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This configuration is how `logrus` was intended to be used, but JSON in
|
|
||||||
production is mostly only useful if you do log aggregation with tools like
|
|
||||||
Splunk or Logstash.
|
|
||||||
|
|
||||||
#### Formatters
|
|
||||||
|
|
||||||
The built-in logging formatters are:
|
|
||||||
|
|
||||||
* `logrus.TextFormatter`. Logs the event in colors if stdout is a tty, otherwise
|
|
||||||
without colors.
|
|
||||||
* *Note:* to force colored output when there is no TTY, set the `ForceColors`
|
|
||||||
field to `true`. To force no colored output even if there is a TTY set the
|
|
||||||
`DisableColors` field to `true`. For Windows, see
|
|
||||||
[github.com/mattn/go-colorable](https://github.com/mattn/go-colorable).
|
|
||||||
* When colors are enabled, levels are truncated to 4 characters by default. To disable
|
|
||||||
truncation set the `DisableLevelTruncation` field to `true`.
|
|
||||||
* When outputting to a TTY, it's often helpful to visually scan down a column where all the levels are the same width. Setting the `PadLevelText` field to `true` enables this behavior, by adding padding to the level text.
|
|
||||||
* All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#TextFormatter).
|
|
||||||
* `logrus.JSONFormatter`. Logs fields as JSON.
|
|
||||||
* All options are listed in the [generated docs](https://godoc.org/github.com/sirupsen/logrus#JSONFormatter).
|
|
||||||
|
|
||||||
Third party logging formatters:
|
|
||||||
|
|
||||||
* [`FluentdFormatter`](https://github.com/joonix/log). Formats entries that can be parsed by Kubernetes and Google Container Engine.
|
|
||||||
* [`GELF`](https://github.com/fabienm/go-logrus-formatters). Formats entries so they comply to Graylog's [GELF 1.1 specification](http://docs.graylog.org/en/2.4/pages/gelf.html).
|
|
||||||
* [`logstash`](https://github.com/bshuster-repo/logrus-logstash-hook). Logs fields as [Logstash](http://logstash.net) Events.
|
|
||||||
* [`prefixed`](https://github.com/x-cray/logrus-prefixed-formatter). Displays log entry source along with alternative layout.
|
|
||||||
* [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the Power of Zalgo.
|
|
||||||
* [`nested-logrus-formatter`](https://github.com/antonfisher/nested-logrus-formatter). Converts logrus fields to a nested structure.
|
|
||||||
* [`powerful-logrus-formatter`](https://github.com/zput/zxcTool). get fileName, log's line number and the latest function's name when print log; Sava log to files.
|
|
||||||
* [`caption-json-formatter`](https://github.com/nolleh/caption_json_formatter). logrus's message json formatter with human-readable caption added.
|
|
||||||
|
|
||||||
You can define your formatter by implementing the `Formatter` interface,
|
|
||||||
requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a
|
|
||||||
`Fields` type (`map[string]interface{}`) with all your fields as well as the
|
|
||||||
default ones (see Entries section above):
|
|
||||||
|
|
||||||
```go
|
|
||||||
type MyJSONFormatter struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
log.SetFormatter(new(MyJSONFormatter))
|
|
||||||
|
|
||||||
func (f *MyJSONFormatter) Format(entry *Entry) ([]byte, error) {
|
|
||||||
// Note this doesn't include Time, Level and Message which are available on
|
|
||||||
// the Entry. Consult `godoc` on information about those fields or read the
|
|
||||||
// source of the official loggers.
|
|
||||||
serialized, err := json.Marshal(entry.Data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Failed to marshal fields to JSON, %w", err)
|
|
||||||
}
|
|
||||||
return append(serialized, '\n'), nil
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Logger as an `io.Writer`
|
|
||||||
|
|
||||||
Logrus can be transformed into an `io.Writer`. That writer is the end of an `io.Pipe` and it is your responsibility to close it.
|
|
||||||
|
|
||||||
```go
|
|
||||||
w := logger.Writer()
|
|
||||||
defer w.Close()
|
|
||||||
|
|
||||||
srv := http.Server{
|
|
||||||
// create a stdlib log.Logger that writes to
|
|
||||||
// logrus.Logger.
|
|
||||||
ErrorLog: log.New(w, "", 0),
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Each line written to that writer will be printed the usual way, using formatters
|
|
||||||
and hooks. The level for those entries is `info`.
|
|
||||||
|
|
||||||
This means that we can override the standard library logger easily:
|
|
||||||
|
|
||||||
```go
|
|
||||||
logger := logrus.New()
|
|
||||||
logger.Formatter = &logrus.JSONFormatter{}
|
|
||||||
|
|
||||||
// Use logrus for standard log output
|
|
||||||
// Note that `log` here references stdlib's log
|
|
||||||
// Not logrus imported under the name `log`.
|
|
||||||
log.SetOutput(logger.Writer())
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Rotation
|
|
||||||
|
|
||||||
Log rotation is not provided with Logrus. Log rotation should be done by an
|
|
||||||
external program (like `logrotate(8)`) that can compress and delete old log
|
|
||||||
entries. It should not be a feature of the application-level logger.
|
|
||||||
|
|
||||||
#### Tools
|
|
||||||
|
|
||||||
| Tool | Description |
|
|
||||||
| ---- | ----------- |
|
|
||||||
|[Logrus Mate](https://github.com/gogap/logrus_mate)|Logrus mate is a tool for Logrus to manage loggers, you can initial logger's level, hook and formatter by config file, the logger will be generated with different configs in different environments.|
|
|
||||||
|[Logrus Viper Helper](https://github.com/heirko/go-contrib/tree/master/logrusHelper)|An Helper around Logrus to wrap with spf13/Viper to load configuration with fangs! And to simplify Logrus configuration use some behavior of [Logrus Mate](https://github.com/gogap/logrus_mate). [sample](https://github.com/heirko/iris-contrib/blob/master/middleware/logrus-logger/example) |
|
|
||||||
|
|
||||||
#### Testing
|
|
||||||
|
|
||||||
Logrus has a built in facility for asserting the presence of log messages. This is implemented through the `test` hook and provides:
|
|
||||||
|
|
||||||
* decorators for existing logger (`test.NewLocal` and `test.NewGlobal`) which basically just adds the `test` hook
|
|
||||||
* a test logger (`test.NewNullLogger`) that just records log messages (and does not output any):
|
|
||||||
|
|
||||||
```go
|
|
||||||
import(
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"github.com/sirupsen/logrus/hooks/test"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSomething(t*testing.T){
|
|
||||||
logger, hook := test.NewNullLogger()
|
|
||||||
logger.Error("Helloerror")
|
|
||||||
|
|
||||||
assert.Equal(t, 1, len(hook.Entries))
|
|
||||||
assert.Equal(t, logrus.ErrorLevel, hook.LastEntry().Level)
|
|
||||||
assert.Equal(t, "Helloerror", hook.LastEntry().Message)
|
|
||||||
|
|
||||||
hook.Reset()
|
|
||||||
assert.Nil(t, hook.LastEntry())
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Fatal handlers
|
|
||||||
|
|
||||||
Logrus can register one or more functions that will be called when any `fatal`
|
|
||||||
level message is logged. The registered handlers will be executed before
|
|
||||||
logrus performs an `os.Exit(1)`. This behavior may be helpful if callers need
|
|
||||||
to gracefully shutdown. Unlike a `panic("Something went wrong...")` call which can be intercepted with a deferred `recover` a call to `os.Exit(1)` can not be intercepted.
|
|
||||||
|
|
||||||
```
|
|
||||||
...
|
|
||||||
handler := func() {
|
|
||||||
// gracefully shutdown something...
|
|
||||||
}
|
|
||||||
logrus.RegisterExitHandler(handler)
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Thread safety
|
|
||||||
|
|
||||||
By default, Logger is protected by a mutex for concurrent writes. The mutex is held when calling hooks and writing logs.
|
|
||||||
If you are sure such locking is not needed, you can call logger.SetNoLock() to disable the locking.
|
|
||||||
|
|
||||||
Situation when locking is not needed includes:
|
|
||||||
|
|
||||||
* You have no hooks registered, or hooks calling is already thread-safe.
|
|
||||||
|
|
||||||
* Writing to logger.Out is already thread-safe, for example:
|
|
||||||
|
|
||||||
1) logger.Out is protected by locks.
|
|
||||||
|
|
||||||
2) logger.Out is an os.File handler opened with `O_APPEND` flag, and every write is smaller than 4k. (This allows multi-thread/multi-process writing)
|
|
||||||
|
|
||||||
(Refer to http://www.notthewizard.com/2014/06/17/are-files-appends-really-atomic/)
|
|
||||||
76
vendor/github.com/sirupsen/logrus/alt_exit.go
generated
vendored
76
vendor/github.com/sirupsen/logrus/alt_exit.go
generated
vendored
@@ -1,76 +0,0 @@
|
|||||||
package logrus
|
|
||||||
|
|
||||||
// The following code was sourced and modified from the
|
|
||||||
// https://github.com/tebeka/atexit package governed by the following license:
|
|
||||||
//
|
|
||||||
// Copyright (c) 2012 Miki Tebeka <miki.tebeka@gmail.com>.
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
||||||
// this software and associated documentation files (the "Software"), to deal in
|
|
||||||
// the Software without restriction, including without limitation the rights to
|
|
||||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
||||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
|
||||||
// subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in all
|
|
||||||
// copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
||||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
||||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
||||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
||||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
var handlers = []func(){}
|
|
||||||
|
|
||||||
func runHandler(handler func()) {
|
|
||||||
defer func() {
|
|
||||||
if err := recover(); err != nil {
|
|
||||||
fmt.Fprintln(os.Stderr, "Error: Logrus exit handler error:", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
handler()
|
|
||||||
}
|
|
||||||
|
|
||||||
func runHandlers() {
|
|
||||||
for _, handler := range handlers {
|
|
||||||
runHandler(handler)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exit runs all the Logrus atexit handlers and then terminates the program using os.Exit(code)
|
|
||||||
func Exit(code int) {
|
|
||||||
runHandlers()
|
|
||||||
os.Exit(code)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterExitHandler appends a Logrus Exit handler to the list of handlers,
|
|
||||||
// call logrus.Exit to invoke all handlers. The handlers will also be invoked when
|
|
||||||
// any Fatal log entry is made.
|
|
||||||
//
|
|
||||||
// This method is useful when a caller wishes to use logrus to log a fatal
|
|
||||||
// message but also needs to gracefully shutdown. An example usecase could be
|
|
||||||
// closing database connections, or sending a alert that the application is
|
|
||||||
// closing.
|
|
||||||
func RegisterExitHandler(handler func()) {
|
|
||||||
handlers = append(handlers, handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeferExitHandler prepends a Logrus Exit handler to the list of handlers,
|
|
||||||
// call logrus.Exit to invoke all handlers. The handlers will also be invoked when
|
|
||||||
// any Fatal log entry is made.
|
|
||||||
//
|
|
||||||
// This method is useful when a caller wishes to use logrus to log a fatal
|
|
||||||
// message but also needs to gracefully shutdown. An example usecase could be
|
|
||||||
// closing database connections, or sending a alert that the application is
|
|
||||||
// closing.
|
|
||||||
func DeferExitHandler(handler func()) {
|
|
||||||
handlers = append([]func(){handler}, handlers...)
|
|
||||||
}
|
|
||||||
14
vendor/github.com/sirupsen/logrus/appveyor.yml
generated
vendored
14
vendor/github.com/sirupsen/logrus/appveyor.yml
generated
vendored
@@ -1,14 +0,0 @@
|
|||||||
version: "{build}"
|
|
||||||
platform: x64
|
|
||||||
clone_folder: c:\gopath\src\github.com\sirupsen\logrus
|
|
||||||
environment:
|
|
||||||
GOPATH: c:\gopath
|
|
||||||
branches:
|
|
||||||
only:
|
|
||||||
- master
|
|
||||||
install:
|
|
||||||
- set PATH=%GOPATH%\bin;c:\go\bin;%PATH%
|
|
||||||
- go version
|
|
||||||
build_script:
|
|
||||||
- go get -t
|
|
||||||
- go test
|
|
||||||
43
vendor/github.com/sirupsen/logrus/buffer_pool.go
generated
vendored
43
vendor/github.com/sirupsen/logrus/buffer_pool.go
generated
vendored
@@ -1,43 +0,0 @@
|
|||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
bufferPool BufferPool
|
|
||||||
)
|
|
||||||
|
|
||||||
type BufferPool interface {
|
|
||||||
Put(*bytes.Buffer)
|
|
||||||
Get() *bytes.Buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
type defaultPool struct {
|
|
||||||
pool *sync.Pool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *defaultPool) Put(buf *bytes.Buffer) {
|
|
||||||
p.pool.Put(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *defaultPool) Get() *bytes.Buffer {
|
|
||||||
return p.pool.Get().(*bytes.Buffer)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetBufferPool allows to replace the default logrus buffer pool
|
|
||||||
// to better meets the specific needs of an application.
|
|
||||||
func SetBufferPool(bp BufferPool) {
|
|
||||||
bufferPool = bp
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
SetBufferPool(&defaultPool{
|
|
||||||
pool: &sync.Pool{
|
|
||||||
New: func() interface{} {
|
|
||||||
return new(bytes.Buffer)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
26
vendor/github.com/sirupsen/logrus/doc.go
generated
vendored
26
vendor/github.com/sirupsen/logrus/doc.go
generated
vendored
@@ -1,26 +0,0 @@
|
|||||||
/*
|
|
||||||
Package logrus is a structured logger for Go, completely API compatible with the standard library logger.
|
|
||||||
|
|
||||||
|
|
||||||
The simplest way to use Logrus is simply the package-level exported logger:
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
log.WithFields(log.Fields{
|
|
||||||
"animal": "walrus",
|
|
||||||
"number": 1,
|
|
||||||
"size": 10,
|
|
||||||
}).Info("A walrus appears")
|
|
||||||
}
|
|
||||||
|
|
||||||
Output:
|
|
||||||
time="2015-09-07T08:48:33Z" level=info msg="A walrus appears" animal=walrus number=1 size=10
|
|
||||||
|
|
||||||
For a full guide visit https://github.com/sirupsen/logrus
|
|
||||||
*/
|
|
||||||
package logrus
|
|
||||||
442
vendor/github.com/sirupsen/logrus/entry.go
generated
vendored
442
vendor/github.com/sirupsen/logrus/entry.go
generated
vendored
@@ -1,442 +0,0 @@
|
|||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"reflect"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
|
|
||||||
// qualified package name, cached at first use
|
|
||||||
logrusPackage string
|
|
||||||
|
|
||||||
// Positions in the call stack when tracing to report the calling method
|
|
||||||
minimumCallerDepth int
|
|
||||||
|
|
||||||
// Used for caller information initialisation
|
|
||||||
callerInitOnce sync.Once
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
maximumCallerDepth int = 25
|
|
||||||
knownLogrusFrames int = 4
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
// start at the bottom of the stack before the package-name cache is primed
|
|
||||||
minimumCallerDepth = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Defines the key when adding errors using WithError.
|
|
||||||
var ErrorKey = "error"
|
|
||||||
|
|
||||||
// An entry is the final or intermediate Logrus logging entry. It contains all
|
|
||||||
// the fields passed with WithField{,s}. It's finally logged when Trace, Debug,
|
|
||||||
// Info, Warn, Error, Fatal or Panic is called on it. These objects can be
|
|
||||||
// reused and passed around as much as you wish to avoid field duplication.
|
|
||||||
type Entry struct {
|
|
||||||
Logger *Logger
|
|
||||||
|
|
||||||
// Contains all the fields set by the user.
|
|
||||||
Data Fields
|
|
||||||
|
|
||||||
// Time at which the log entry was created
|
|
||||||
Time time.Time
|
|
||||||
|
|
||||||
// Level the log entry was logged at: Trace, Debug, Info, Warn, Error, Fatal or Panic
|
|
||||||
// This field will be set on entry firing and the value will be equal to the one in Logger struct field.
|
|
||||||
Level Level
|
|
||||||
|
|
||||||
// Calling method, with package name
|
|
||||||
Caller *runtime.Frame
|
|
||||||
|
|
||||||
// Message passed to Trace, Debug, Info, Warn, Error, Fatal or Panic
|
|
||||||
Message string
|
|
||||||
|
|
||||||
// When formatter is called in entry.log(), a Buffer may be set to entry
|
|
||||||
Buffer *bytes.Buffer
|
|
||||||
|
|
||||||
// Contains the context set by the user. Useful for hook processing etc.
|
|
||||||
Context context.Context
|
|
||||||
|
|
||||||
// err may contain a field formatting error
|
|
||||||
err string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewEntry(logger *Logger) *Entry {
|
|
||||||
return &Entry{
|
|
||||||
Logger: logger,
|
|
||||||
// Default is three fields, plus one optional. Give a little extra room.
|
|
||||||
Data: make(Fields, 6),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Dup() *Entry {
|
|
||||||
data := make(Fields, len(entry.Data))
|
|
||||||
for k, v := range entry.Data {
|
|
||||||
data[k] = v
|
|
||||||
}
|
|
||||||
return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, Context: entry.Context, err: entry.err}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the bytes representation of this entry from the formatter.
|
|
||||||
func (entry *Entry) Bytes() ([]byte, error) {
|
|
||||||
return entry.Logger.Formatter.Format(entry)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the string representation from the reader and ultimately the
|
|
||||||
// formatter.
|
|
||||||
func (entry *Entry) String() (string, error) {
|
|
||||||
serialized, err := entry.Bytes()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
str := string(serialized)
|
|
||||||
return str, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add an error as single field (using the key defined in ErrorKey) to the Entry.
|
|
||||||
func (entry *Entry) WithError(err error) *Entry {
|
|
||||||
return entry.WithField(ErrorKey, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a context to the Entry.
|
|
||||||
func (entry *Entry) WithContext(ctx context.Context) *Entry {
|
|
||||||
dataCopy := make(Fields, len(entry.Data))
|
|
||||||
for k, v := range entry.Data {
|
|
||||||
dataCopy[k] = v
|
|
||||||
}
|
|
||||||
return &Entry{Logger: entry.Logger, Data: dataCopy, Time: entry.Time, err: entry.err, Context: ctx}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a single field to the Entry.
|
|
||||||
func (entry *Entry) WithField(key string, value interface{}) *Entry {
|
|
||||||
return entry.WithFields(Fields{key: value})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a map of fields to the Entry.
|
|
||||||
func (entry *Entry) WithFields(fields Fields) *Entry {
|
|
||||||
data := make(Fields, len(entry.Data)+len(fields))
|
|
||||||
for k, v := range entry.Data {
|
|
||||||
data[k] = v
|
|
||||||
}
|
|
||||||
fieldErr := entry.err
|
|
||||||
for k, v := range fields {
|
|
||||||
isErrField := false
|
|
||||||
if t := reflect.TypeOf(v); t != nil {
|
|
||||||
switch {
|
|
||||||
case t.Kind() == reflect.Func, t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Func:
|
|
||||||
isErrField = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if isErrField {
|
|
||||||
tmp := fmt.Sprintf("can not add field %q", k)
|
|
||||||
if fieldErr != "" {
|
|
||||||
fieldErr = entry.err + ", " + tmp
|
|
||||||
} else {
|
|
||||||
fieldErr = tmp
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
data[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, err: fieldErr, Context: entry.Context}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Overrides the time of the Entry.
|
|
||||||
func (entry *Entry) WithTime(t time.Time) *Entry {
|
|
||||||
dataCopy := make(Fields, len(entry.Data))
|
|
||||||
for k, v := range entry.Data {
|
|
||||||
dataCopy[k] = v
|
|
||||||
}
|
|
||||||
return &Entry{Logger: entry.Logger, Data: dataCopy, Time: t, err: entry.err, Context: entry.Context}
|
|
||||||
}
|
|
||||||
|
|
||||||
// getPackageName reduces a fully qualified function name to the package name
|
|
||||||
// There really ought to be to be a better way...
|
|
||||||
func getPackageName(f string) string {
|
|
||||||
for {
|
|
||||||
lastPeriod := strings.LastIndex(f, ".")
|
|
||||||
lastSlash := strings.LastIndex(f, "/")
|
|
||||||
if lastPeriod > lastSlash {
|
|
||||||
f = f[:lastPeriod]
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
// getCaller retrieves the name of the first non-logrus calling function
|
|
||||||
func getCaller() *runtime.Frame {
|
|
||||||
// cache this package's fully-qualified name
|
|
||||||
callerInitOnce.Do(func() {
|
|
||||||
pcs := make([]uintptr, maximumCallerDepth)
|
|
||||||
_ = runtime.Callers(0, pcs)
|
|
||||||
|
|
||||||
// dynamic get the package name and the minimum caller depth
|
|
||||||
for i := 0; i < maximumCallerDepth; i++ {
|
|
||||||
funcName := runtime.FuncForPC(pcs[i]).Name()
|
|
||||||
if strings.Contains(funcName, "getCaller") {
|
|
||||||
logrusPackage = getPackageName(funcName)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
minimumCallerDepth = knownLogrusFrames
|
|
||||||
})
|
|
||||||
|
|
||||||
// Restrict the lookback frames to avoid runaway lookups
|
|
||||||
pcs := make([]uintptr, maximumCallerDepth)
|
|
||||||
depth := runtime.Callers(minimumCallerDepth, pcs)
|
|
||||||
frames := runtime.CallersFrames(pcs[:depth])
|
|
||||||
|
|
||||||
for f, again := frames.Next(); again; f, again = frames.Next() {
|
|
||||||
pkg := getPackageName(f.Function)
|
|
||||||
|
|
||||||
// If the caller isn't part of this package, we're done
|
|
||||||
if pkg != logrusPackage {
|
|
||||||
return &f //nolint:scopelint
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we got here, we failed to find the caller's context
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry Entry) HasCaller() (has bool) {
|
|
||||||
return entry.Logger != nil &&
|
|
||||||
entry.Logger.ReportCaller &&
|
|
||||||
entry.Caller != nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) log(level Level, msg string) {
|
|
||||||
var buffer *bytes.Buffer
|
|
||||||
|
|
||||||
newEntry := entry.Dup()
|
|
||||||
|
|
||||||
if newEntry.Time.IsZero() {
|
|
||||||
newEntry.Time = time.Now()
|
|
||||||
}
|
|
||||||
|
|
||||||
newEntry.Level = level
|
|
||||||
newEntry.Message = msg
|
|
||||||
|
|
||||||
newEntry.Logger.mu.Lock()
|
|
||||||
reportCaller := newEntry.Logger.ReportCaller
|
|
||||||
bufPool := newEntry.getBufferPool()
|
|
||||||
newEntry.Logger.mu.Unlock()
|
|
||||||
|
|
||||||
if reportCaller {
|
|
||||||
newEntry.Caller = getCaller()
|
|
||||||
}
|
|
||||||
|
|
||||||
newEntry.fireHooks()
|
|
||||||
buffer = bufPool.Get()
|
|
||||||
defer func() {
|
|
||||||
newEntry.Buffer = nil
|
|
||||||
buffer.Reset()
|
|
||||||
bufPool.Put(buffer)
|
|
||||||
}()
|
|
||||||
buffer.Reset()
|
|
||||||
newEntry.Buffer = buffer
|
|
||||||
|
|
||||||
newEntry.write()
|
|
||||||
|
|
||||||
newEntry.Buffer = nil
|
|
||||||
|
|
||||||
// To avoid Entry#log() returning a value that only would make sense for
|
|
||||||
// panic() to use in Entry#Panic(), we avoid the allocation by checking
|
|
||||||
// directly here.
|
|
||||||
if level <= PanicLevel {
|
|
||||||
panic(newEntry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) getBufferPool() (pool BufferPool) {
|
|
||||||
if entry.Logger.BufferPool != nil {
|
|
||||||
return entry.Logger.BufferPool
|
|
||||||
}
|
|
||||||
return bufferPool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) fireHooks() {
|
|
||||||
var tmpHooks LevelHooks
|
|
||||||
entry.Logger.mu.Lock()
|
|
||||||
tmpHooks = make(LevelHooks, len(entry.Logger.Hooks))
|
|
||||||
for k, v := range entry.Logger.Hooks {
|
|
||||||
tmpHooks[k] = v
|
|
||||||
}
|
|
||||||
entry.Logger.mu.Unlock()
|
|
||||||
|
|
||||||
err := tmpHooks.Fire(entry.Level, entry)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) write() {
|
|
||||||
entry.Logger.mu.Lock()
|
|
||||||
defer entry.Logger.mu.Unlock()
|
|
||||||
serialized, err := entry.Logger.Formatter.Format(entry)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if _, err := entry.Logger.Out.Write(serialized); err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log will log a message at the level given as parameter.
|
|
||||||
// Warning: using Log at Panic or Fatal level will not respectively Panic nor Exit.
|
|
||||||
// For this behaviour Entry.Panic or Entry.Fatal should be used instead.
|
|
||||||
func (entry *Entry) Log(level Level, args ...interface{}) {
|
|
||||||
if entry.Logger.IsLevelEnabled(level) {
|
|
||||||
entry.log(level, fmt.Sprint(args...))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Trace(args ...interface{}) {
|
|
||||||
entry.Log(TraceLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Debug(args ...interface{}) {
|
|
||||||
entry.Log(DebugLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Print(args ...interface{}) {
|
|
||||||
entry.Info(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Info(args ...interface{}) {
|
|
||||||
entry.Log(InfoLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Warn(args ...interface{}) {
|
|
||||||
entry.Log(WarnLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Warning(args ...interface{}) {
|
|
||||||
entry.Warn(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Error(args ...interface{}) {
|
|
||||||
entry.Log(ErrorLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Fatal(args ...interface{}) {
|
|
||||||
entry.Log(FatalLevel, args...)
|
|
||||||
entry.Logger.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Panic(args ...interface{}) {
|
|
||||||
entry.Log(PanicLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Entry Printf family functions
|
|
||||||
|
|
||||||
func (entry *Entry) Logf(level Level, format string, args ...interface{}) {
|
|
||||||
if entry.Logger.IsLevelEnabled(level) {
|
|
||||||
entry.Log(level, fmt.Sprintf(format, args...))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Tracef(format string, args ...interface{}) {
|
|
||||||
entry.Logf(TraceLevel, format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Debugf(format string, args ...interface{}) {
|
|
||||||
entry.Logf(DebugLevel, format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Infof(format string, args ...interface{}) {
|
|
||||||
entry.Logf(InfoLevel, format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Printf(format string, args ...interface{}) {
|
|
||||||
entry.Infof(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Warnf(format string, args ...interface{}) {
|
|
||||||
entry.Logf(WarnLevel, format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Warningf(format string, args ...interface{}) {
|
|
||||||
entry.Warnf(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Errorf(format string, args ...interface{}) {
|
|
||||||
entry.Logf(ErrorLevel, format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Fatalf(format string, args ...interface{}) {
|
|
||||||
entry.Logf(FatalLevel, format, args...)
|
|
||||||
entry.Logger.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Panicf(format string, args ...interface{}) {
|
|
||||||
entry.Logf(PanicLevel, format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Entry Println family functions
|
|
||||||
|
|
||||||
func (entry *Entry) Logln(level Level, args ...interface{}) {
|
|
||||||
if entry.Logger.IsLevelEnabled(level) {
|
|
||||||
entry.Log(level, entry.sprintlnn(args...))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Traceln(args ...interface{}) {
|
|
||||||
entry.Logln(TraceLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Debugln(args ...interface{}) {
|
|
||||||
entry.Logln(DebugLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Infoln(args ...interface{}) {
|
|
||||||
entry.Logln(InfoLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Println(args ...interface{}) {
|
|
||||||
entry.Infoln(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Warnln(args ...interface{}) {
|
|
||||||
entry.Logln(WarnLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Warningln(args ...interface{}) {
|
|
||||||
entry.Warnln(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Errorln(args ...interface{}) {
|
|
||||||
entry.Logln(ErrorLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Fatalln(args ...interface{}) {
|
|
||||||
entry.Logln(FatalLevel, args...)
|
|
||||||
entry.Logger.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (entry *Entry) Panicln(args ...interface{}) {
|
|
||||||
entry.Logln(PanicLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sprintlnn => Sprint no newline. This is to get the behavior of how
|
|
||||||
// fmt.Sprintln where spaces are always added between operands, regardless of
|
|
||||||
// their type. Instead of vendoring the Sprintln implementation to spare a
|
|
||||||
// string allocation, we do the simplest thing.
|
|
||||||
func (entry *Entry) sprintlnn(args ...interface{}) string {
|
|
||||||
msg := fmt.Sprintln(args...)
|
|
||||||
return msg[:len(msg)-1]
|
|
||||||
}
|
|
||||||
270
vendor/github.com/sirupsen/logrus/exported.go
generated
vendored
270
vendor/github.com/sirupsen/logrus/exported.go
generated
vendored
@@ -1,270 +0,0 @@
|
|||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"io"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// std is the name of the standard logger in stdlib `log`
|
|
||||||
std = New()
|
|
||||||
)
|
|
||||||
|
|
||||||
func StandardLogger() *Logger {
|
|
||||||
return std
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetOutput sets the standard logger output.
|
|
||||||
func SetOutput(out io.Writer) {
|
|
||||||
std.SetOutput(out)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetFormatter sets the standard logger formatter.
|
|
||||||
func SetFormatter(formatter Formatter) {
|
|
||||||
std.SetFormatter(formatter)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetReportCaller sets whether the standard logger will include the calling
|
|
||||||
// method as a field.
|
|
||||||
func SetReportCaller(include bool) {
|
|
||||||
std.SetReportCaller(include)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetLevel sets the standard logger level.
|
|
||||||
func SetLevel(level Level) {
|
|
||||||
std.SetLevel(level)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetLevel returns the standard logger level.
|
|
||||||
func GetLevel() Level {
|
|
||||||
return std.GetLevel()
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsLevelEnabled checks if the log level of the standard logger is greater than the level param
|
|
||||||
func IsLevelEnabled(level Level) bool {
|
|
||||||
return std.IsLevelEnabled(level)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddHook adds a hook to the standard logger hooks.
|
|
||||||
func AddHook(hook Hook) {
|
|
||||||
std.AddHook(hook)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithError creates an entry from the standard logger and adds an error to it, using the value defined in ErrorKey as key.
|
|
||||||
func WithError(err error) *Entry {
|
|
||||||
return std.WithField(ErrorKey, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithContext creates an entry from the standard logger and adds a context to it.
|
|
||||||
func WithContext(ctx context.Context) *Entry {
|
|
||||||
return std.WithContext(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithField creates an entry from the standard logger and adds a field to
|
|
||||||
// it. If you want multiple fields, use `WithFields`.
|
|
||||||
//
|
|
||||||
// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
|
|
||||||
// or Panic on the Entry it returns.
|
|
||||||
func WithField(key string, value interface{}) *Entry {
|
|
||||||
return std.WithField(key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithFields creates an entry from the standard logger and adds multiple
|
|
||||||
// fields to it. This is simply a helper for `WithField`, invoking it
|
|
||||||
// once for each field.
|
|
||||||
//
|
|
||||||
// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
|
|
||||||
// or Panic on the Entry it returns.
|
|
||||||
func WithFields(fields Fields) *Entry {
|
|
||||||
return std.WithFields(fields)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithTime creates an entry from the standard logger and overrides the time of
|
|
||||||
// logs generated with it.
|
|
||||||
//
|
|
||||||
// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
|
|
||||||
// or Panic on the Entry it returns.
|
|
||||||
func WithTime(t time.Time) *Entry {
|
|
||||||
return std.WithTime(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trace logs a message at level Trace on the standard logger.
|
|
||||||
func Trace(args ...interface{}) {
|
|
||||||
std.Trace(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debug logs a message at level Debug on the standard logger.
|
|
||||||
func Debug(args ...interface{}) {
|
|
||||||
std.Debug(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print logs a message at level Info on the standard logger.
|
|
||||||
func Print(args ...interface{}) {
|
|
||||||
std.Print(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Info logs a message at level Info on the standard logger.
|
|
||||||
func Info(args ...interface{}) {
|
|
||||||
std.Info(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Warn logs a message at level Warn on the standard logger.
|
|
||||||
func Warn(args ...interface{}) {
|
|
||||||
std.Warn(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Warning logs a message at level Warn on the standard logger.
|
|
||||||
func Warning(args ...interface{}) {
|
|
||||||
std.Warning(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error logs a message at level Error on the standard logger.
|
|
||||||
func Error(args ...interface{}) {
|
|
||||||
std.Error(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Panic logs a message at level Panic on the standard logger.
|
|
||||||
func Panic(args ...interface{}) {
|
|
||||||
std.Panic(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fatal logs a message at level Fatal on the standard logger then the process will exit with status set to 1.
|
|
||||||
func Fatal(args ...interface{}) {
|
|
||||||
std.Fatal(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TraceFn logs a message from a func at level Trace on the standard logger.
|
|
||||||
func TraceFn(fn LogFunction) {
|
|
||||||
std.TraceFn(fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DebugFn logs a message from a func at level Debug on the standard logger.
|
|
||||||
func DebugFn(fn LogFunction) {
|
|
||||||
std.DebugFn(fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrintFn logs a message from a func at level Info on the standard logger.
|
|
||||||
func PrintFn(fn LogFunction) {
|
|
||||||
std.PrintFn(fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// InfoFn logs a message from a func at level Info on the standard logger.
|
|
||||||
func InfoFn(fn LogFunction) {
|
|
||||||
std.InfoFn(fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WarnFn logs a message from a func at level Warn on the standard logger.
|
|
||||||
func WarnFn(fn LogFunction) {
|
|
||||||
std.WarnFn(fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WarningFn logs a message from a func at level Warn on the standard logger.
|
|
||||||
func WarningFn(fn LogFunction) {
|
|
||||||
std.WarningFn(fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ErrorFn logs a message from a func at level Error on the standard logger.
|
|
||||||
func ErrorFn(fn LogFunction) {
|
|
||||||
std.ErrorFn(fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PanicFn logs a message from a func at level Panic on the standard logger.
|
|
||||||
func PanicFn(fn LogFunction) {
|
|
||||||
std.PanicFn(fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FatalFn logs a message from a func at level Fatal on the standard logger then the process will exit with status set to 1.
|
|
||||||
func FatalFn(fn LogFunction) {
|
|
||||||
std.FatalFn(fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tracef logs a message at level Trace on the standard logger.
|
|
||||||
func Tracef(format string, args ...interface{}) {
|
|
||||||
std.Tracef(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debugf logs a message at level Debug on the standard logger.
|
|
||||||
func Debugf(format string, args ...interface{}) {
|
|
||||||
std.Debugf(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Printf logs a message at level Info on the standard logger.
|
|
||||||
func Printf(format string, args ...interface{}) {
|
|
||||||
std.Printf(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Infof logs a message at level Info on the standard logger.
|
|
||||||
func Infof(format string, args ...interface{}) {
|
|
||||||
std.Infof(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Warnf logs a message at level Warn on the standard logger.
|
|
||||||
func Warnf(format string, args ...interface{}) {
|
|
||||||
std.Warnf(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Warningf logs a message at level Warn on the standard logger.
|
|
||||||
func Warningf(format string, args ...interface{}) {
|
|
||||||
std.Warningf(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Errorf logs a message at level Error on the standard logger.
|
|
||||||
func Errorf(format string, args ...interface{}) {
|
|
||||||
std.Errorf(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Panicf logs a message at level Panic on the standard logger.
|
|
||||||
func Panicf(format string, args ...interface{}) {
|
|
||||||
std.Panicf(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fatalf logs a message at level Fatal on the standard logger then the process will exit with status set to 1.
|
|
||||||
func Fatalf(format string, args ...interface{}) {
|
|
||||||
std.Fatalf(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Traceln logs a message at level Trace on the standard logger.
|
|
||||||
func Traceln(args ...interface{}) {
|
|
||||||
std.Traceln(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debugln logs a message at level Debug on the standard logger.
|
|
||||||
func Debugln(args ...interface{}) {
|
|
||||||
std.Debugln(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Println logs a message at level Info on the standard logger.
|
|
||||||
func Println(args ...interface{}) {
|
|
||||||
std.Println(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Infoln logs a message at level Info on the standard logger.
|
|
||||||
func Infoln(args ...interface{}) {
|
|
||||||
std.Infoln(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Warnln logs a message at level Warn on the standard logger.
|
|
||||||
func Warnln(args ...interface{}) {
|
|
||||||
std.Warnln(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Warningln logs a message at level Warn on the standard logger.
|
|
||||||
func Warningln(args ...interface{}) {
|
|
||||||
std.Warningln(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Errorln logs a message at level Error on the standard logger.
|
|
||||||
func Errorln(args ...interface{}) {
|
|
||||||
std.Errorln(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Panicln logs a message at level Panic on the standard logger.
|
|
||||||
func Panicln(args ...interface{}) {
|
|
||||||
std.Panicln(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fatalln logs a message at level Fatal on the standard logger then the process will exit with status set to 1.
|
|
||||||
func Fatalln(args ...interface{}) {
|
|
||||||
std.Fatalln(args...)
|
|
||||||
}
|
|
||||||
78
vendor/github.com/sirupsen/logrus/formatter.go
generated
vendored
78
vendor/github.com/sirupsen/logrus/formatter.go
generated
vendored
@@ -1,78 +0,0 @@
|
|||||||
package logrus
|
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
// Default key names for the default fields
|
|
||||||
const (
|
|
||||||
defaultTimestampFormat = time.RFC3339
|
|
||||||
FieldKeyMsg = "msg"
|
|
||||||
FieldKeyLevel = "level"
|
|
||||||
FieldKeyTime = "time"
|
|
||||||
FieldKeyLogrusError = "logrus_error"
|
|
||||||
FieldKeyFunc = "func"
|
|
||||||
FieldKeyFile = "file"
|
|
||||||
)
|
|
||||||
|
|
||||||
// The Formatter interface is used to implement a custom Formatter. It takes an
|
|
||||||
// `Entry`. It exposes all the fields, including the default ones:
|
|
||||||
//
|
|
||||||
// * `entry.Data["msg"]`. The message passed from Info, Warn, Error ..
|
|
||||||
// * `entry.Data["time"]`. The timestamp.
|
|
||||||
// * `entry.Data["level"]. The level the entry was logged at.
|
|
||||||
//
|
|
||||||
// Any additional fields added with `WithField` or `WithFields` are also in
|
|
||||||
// `entry.Data`. Format is expected to return an array of bytes which are then
|
|
||||||
// logged to `logger.Out`.
|
|
||||||
type Formatter interface {
|
|
||||||
Format(*Entry) ([]byte, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is to not silently overwrite `time`, `msg`, `func` and `level` fields when
|
|
||||||
// dumping it. If this code wasn't there doing:
|
|
||||||
//
|
|
||||||
// logrus.WithField("level", 1).Info("hello")
|
|
||||||
//
|
|
||||||
// Would just silently drop the user provided level. Instead with this code
|
|
||||||
// it'll logged as:
|
|
||||||
//
|
|
||||||
// {"level": "info", "fields.level": 1, "msg": "hello", "time": "..."}
|
|
||||||
//
|
|
||||||
// It's not exported because it's still using Data in an opinionated way. It's to
|
|
||||||
// avoid code duplication between the two default formatters.
|
|
||||||
func prefixFieldClashes(data Fields, fieldMap FieldMap, reportCaller bool) {
|
|
||||||
timeKey := fieldMap.resolve(FieldKeyTime)
|
|
||||||
if t, ok := data[timeKey]; ok {
|
|
||||||
data["fields."+timeKey] = t
|
|
||||||
delete(data, timeKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
msgKey := fieldMap.resolve(FieldKeyMsg)
|
|
||||||
if m, ok := data[msgKey]; ok {
|
|
||||||
data["fields."+msgKey] = m
|
|
||||||
delete(data, msgKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
levelKey := fieldMap.resolve(FieldKeyLevel)
|
|
||||||
if l, ok := data[levelKey]; ok {
|
|
||||||
data["fields."+levelKey] = l
|
|
||||||
delete(data, levelKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
logrusErrKey := fieldMap.resolve(FieldKeyLogrusError)
|
|
||||||
if l, ok := data[logrusErrKey]; ok {
|
|
||||||
data["fields."+logrusErrKey] = l
|
|
||||||
delete(data, logrusErrKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If reportCaller is not set, 'func' will not conflict.
|
|
||||||
if reportCaller {
|
|
||||||
funcKey := fieldMap.resolve(FieldKeyFunc)
|
|
||||||
if l, ok := data[funcKey]; ok {
|
|
||||||
data["fields."+funcKey] = l
|
|
||||||
}
|
|
||||||
fileKey := fieldMap.resolve(FieldKeyFile)
|
|
||||||
if l, ok := data[fileKey]; ok {
|
|
||||||
data["fields."+fileKey] = l
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
34
vendor/github.com/sirupsen/logrus/hooks.go
generated
vendored
34
vendor/github.com/sirupsen/logrus/hooks.go
generated
vendored
@@ -1,34 +0,0 @@
|
|||||||
package logrus
|
|
||||||
|
|
||||||
// A hook to be fired when logging on the logging levels returned from
|
|
||||||
// `Levels()` on your implementation of the interface. Note that this is not
|
|
||||||
// fired in a goroutine or a channel with workers, you should handle such
|
|
||||||
// functionality yourself if your call is non-blocking and you don't wish for
|
|
||||||
// the logging calls for levels returned from `Levels()` to block.
|
|
||||||
type Hook interface {
|
|
||||||
Levels() []Level
|
|
||||||
Fire(*Entry) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Internal type for storing the hooks on a logger instance.
|
|
||||||
type LevelHooks map[Level][]Hook
|
|
||||||
|
|
||||||
// Add a hook to an instance of logger. This is called with
|
|
||||||
// `log.Hooks.Add(new(MyHook))` where `MyHook` implements the `Hook` interface.
|
|
||||||
func (hooks LevelHooks) Add(hook Hook) {
|
|
||||||
for _, level := range hook.Levels() {
|
|
||||||
hooks[level] = append(hooks[level], hook)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fire all the hooks for the passed level. Used by `entry.log` to fire
|
|
||||||
// appropriate hooks for a log entry.
|
|
||||||
func (hooks LevelHooks) Fire(level Level, entry *Entry) error {
|
|
||||||
for _, hook := range hooks[level] {
|
|
||||||
if err := hook.Fire(entry); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
128
vendor/github.com/sirupsen/logrus/json_formatter.go
generated
vendored
128
vendor/github.com/sirupsen/logrus/json_formatter.go
generated
vendored
@@ -1,128 +0,0 @@
|
|||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"runtime"
|
|
||||||
)
|
|
||||||
|
|
||||||
type fieldKey string
|
|
||||||
|
|
||||||
// FieldMap allows customization of the key names for default fields.
|
|
||||||
type FieldMap map[fieldKey]string
|
|
||||||
|
|
||||||
func (f FieldMap) resolve(key fieldKey) string {
|
|
||||||
if k, ok := f[key]; ok {
|
|
||||||
return k
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// JSONFormatter formats logs into parsable json
|
|
||||||
type JSONFormatter struct {
|
|
||||||
// TimestampFormat sets the format used for marshaling timestamps.
|
|
||||||
// The format to use is the same than for time.Format or time.Parse from the standard
|
|
||||||
// library.
|
|
||||||
// The standard Library already provides a set of predefined format.
|
|
||||||
TimestampFormat string
|
|
||||||
|
|
||||||
// DisableTimestamp allows disabling automatic timestamps in output
|
|
||||||
DisableTimestamp bool
|
|
||||||
|
|
||||||
// DisableHTMLEscape allows disabling html escaping in output
|
|
||||||
DisableHTMLEscape bool
|
|
||||||
|
|
||||||
// DataKey allows users to put all the log entry parameters into a nested dictionary at a given key.
|
|
||||||
DataKey string
|
|
||||||
|
|
||||||
// FieldMap allows users to customize the names of keys for default fields.
|
|
||||||
// As an example:
|
|
||||||
// formatter := &JSONFormatter{
|
|
||||||
// FieldMap: FieldMap{
|
|
||||||
// FieldKeyTime: "@timestamp",
|
|
||||||
// FieldKeyLevel: "@level",
|
|
||||||
// FieldKeyMsg: "@message",
|
|
||||||
// FieldKeyFunc: "@caller",
|
|
||||||
// },
|
|
||||||
// }
|
|
||||||
FieldMap FieldMap
|
|
||||||
|
|
||||||
// CallerPrettyfier can be set by the user to modify the content
|
|
||||||
// of the function and file keys in the json data when ReportCaller is
|
|
||||||
// activated. If any of the returned value is the empty string the
|
|
||||||
// corresponding key will be removed from json fields.
|
|
||||||
CallerPrettyfier func(*runtime.Frame) (function string, file string)
|
|
||||||
|
|
||||||
// PrettyPrint will indent all json logs
|
|
||||||
PrettyPrint bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Format renders a single log entry
|
|
||||||
func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
|
|
||||||
data := make(Fields, len(entry.Data)+4)
|
|
||||||
for k, v := range entry.Data {
|
|
||||||
switch v := v.(type) {
|
|
||||||
case error:
|
|
||||||
// Otherwise errors are ignored by `encoding/json`
|
|
||||||
// https://github.com/sirupsen/logrus/issues/137
|
|
||||||
data[k] = v.Error()
|
|
||||||
default:
|
|
||||||
data[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.DataKey != "" {
|
|
||||||
newData := make(Fields, 4)
|
|
||||||
newData[f.DataKey] = data
|
|
||||||
data = newData
|
|
||||||
}
|
|
||||||
|
|
||||||
prefixFieldClashes(data, f.FieldMap, entry.HasCaller())
|
|
||||||
|
|
||||||
timestampFormat := f.TimestampFormat
|
|
||||||
if timestampFormat == "" {
|
|
||||||
timestampFormat = defaultTimestampFormat
|
|
||||||
}
|
|
||||||
|
|
||||||
if entry.err != "" {
|
|
||||||
data[f.FieldMap.resolve(FieldKeyLogrusError)] = entry.err
|
|
||||||
}
|
|
||||||
if !f.DisableTimestamp {
|
|
||||||
data[f.FieldMap.resolve(FieldKeyTime)] = entry.Time.Format(timestampFormat)
|
|
||||||
}
|
|
||||||
data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message
|
|
||||||
data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String()
|
|
||||||
if entry.HasCaller() {
|
|
||||||
funcVal := entry.Caller.Function
|
|
||||||
fileVal := fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line)
|
|
||||||
if f.CallerPrettyfier != nil {
|
|
||||||
funcVal, fileVal = f.CallerPrettyfier(entry.Caller)
|
|
||||||
}
|
|
||||||
if funcVal != "" {
|
|
||||||
data[f.FieldMap.resolve(FieldKeyFunc)] = funcVal
|
|
||||||
}
|
|
||||||
if fileVal != "" {
|
|
||||||
data[f.FieldMap.resolve(FieldKeyFile)] = fileVal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var b *bytes.Buffer
|
|
||||||
if entry.Buffer != nil {
|
|
||||||
b = entry.Buffer
|
|
||||||
} else {
|
|
||||||
b = &bytes.Buffer{}
|
|
||||||
}
|
|
||||||
|
|
||||||
encoder := json.NewEncoder(b)
|
|
||||||
encoder.SetEscapeHTML(!f.DisableHTMLEscape)
|
|
||||||
if f.PrettyPrint {
|
|
||||||
encoder.SetIndent("", " ")
|
|
||||||
}
|
|
||||||
if err := encoder.Encode(data); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to marshal fields to JSON, %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return b.Bytes(), nil
|
|
||||||
}
|
|
||||||
417
vendor/github.com/sirupsen/logrus/logger.go
generated
vendored
417
vendor/github.com/sirupsen/logrus/logger.go
generated
vendored
@@ -1,417 +0,0 @@
|
|||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// LogFunction For big messages, it can be more efficient to pass a function
|
|
||||||
// and only call it if the log level is actually enables rather than
|
|
||||||
// generating the log message and then checking if the level is enabled
|
|
||||||
type LogFunction func() []interface{}
|
|
||||||
|
|
||||||
type Logger struct {
|
|
||||||
// The logs are `io.Copy`'d to this in a mutex. It's common to set this to a
|
|
||||||
// file, or leave it default which is `os.Stderr`. You can also set this to
|
|
||||||
// something more adventurous, such as logging to Kafka.
|
|
||||||
Out io.Writer
|
|
||||||
// Hooks for the logger instance. These allow firing events based on logging
|
|
||||||
// levels and log entries. For example, to send errors to an error tracking
|
|
||||||
// service, log to StatsD or dump the core on fatal errors.
|
|
||||||
Hooks LevelHooks
|
|
||||||
// All log entries pass through the formatter before logged to Out. The
|
|
||||||
// included formatters are `TextFormatter` and `JSONFormatter` for which
|
|
||||||
// TextFormatter is the default. In development (when a TTY is attached) it
|
|
||||||
// logs with colors, but to a file it wouldn't. You can easily implement your
|
|
||||||
// own that implements the `Formatter` interface, see the `README` or included
|
|
||||||
// formatters for examples.
|
|
||||||
Formatter Formatter
|
|
||||||
|
|
||||||
// Flag for whether to log caller info (off by default)
|
|
||||||
ReportCaller bool
|
|
||||||
|
|
||||||
// The logging level the logger should log at. This is typically (and defaults
|
|
||||||
// to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be
|
|
||||||
// logged.
|
|
||||||
Level Level
|
|
||||||
// Used to sync writing to the log. Locking is enabled by Default
|
|
||||||
mu MutexWrap
|
|
||||||
// Reusable empty entry
|
|
||||||
entryPool sync.Pool
|
|
||||||
// Function to exit the application, defaults to `os.Exit()`
|
|
||||||
ExitFunc exitFunc
|
|
||||||
// The buffer pool used to format the log. If it is nil, the default global
|
|
||||||
// buffer pool will be used.
|
|
||||||
BufferPool BufferPool
|
|
||||||
}
|
|
||||||
|
|
||||||
type exitFunc func(int)
|
|
||||||
|
|
||||||
type MutexWrap struct {
|
|
||||||
lock sync.Mutex
|
|
||||||
disabled bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mw *MutexWrap) Lock() {
|
|
||||||
if !mw.disabled {
|
|
||||||
mw.lock.Lock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mw *MutexWrap) Unlock() {
|
|
||||||
if !mw.disabled {
|
|
||||||
mw.lock.Unlock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mw *MutexWrap) Disable() {
|
|
||||||
mw.disabled = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates a new logger. Configuration should be set by changing `Formatter`,
|
|
||||||
// `Out` and `Hooks` directly on the default logger instance. You can also just
|
|
||||||
// instantiate your own:
|
|
||||||
//
|
|
||||||
// var log = &logrus.Logger{
|
|
||||||
// Out: os.Stderr,
|
|
||||||
// Formatter: new(logrus.TextFormatter),
|
|
||||||
// Hooks: make(logrus.LevelHooks),
|
|
||||||
// Level: logrus.DebugLevel,
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// It's recommended to make this a global instance called `log`.
|
|
||||||
func New() *Logger {
|
|
||||||
return &Logger{
|
|
||||||
Out: os.Stderr,
|
|
||||||
Formatter: new(TextFormatter),
|
|
||||||
Hooks: make(LevelHooks),
|
|
||||||
Level: InfoLevel,
|
|
||||||
ExitFunc: os.Exit,
|
|
||||||
ReportCaller: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) newEntry() *Entry {
|
|
||||||
entry, ok := logger.entryPool.Get().(*Entry)
|
|
||||||
if ok {
|
|
||||||
return entry
|
|
||||||
}
|
|
||||||
return NewEntry(logger)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) releaseEntry(entry *Entry) {
|
|
||||||
entry.Data = map[string]interface{}{}
|
|
||||||
logger.entryPool.Put(entry)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithField allocates a new entry and adds a field to it.
|
|
||||||
// Debug, Print, Info, Warn, Error, Fatal or Panic must be then applied to
|
|
||||||
// this new returned entry.
|
|
||||||
// If you want multiple fields, use `WithFields`.
|
|
||||||
func (logger *Logger) WithField(key string, value interface{}) *Entry {
|
|
||||||
entry := logger.newEntry()
|
|
||||||
defer logger.releaseEntry(entry)
|
|
||||||
return entry.WithField(key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adds a struct of fields to the log entry. All it does is call `WithField` for
|
|
||||||
// each `Field`.
|
|
||||||
func (logger *Logger) WithFields(fields Fields) *Entry {
|
|
||||||
entry := logger.newEntry()
|
|
||||||
defer logger.releaseEntry(entry)
|
|
||||||
return entry.WithFields(fields)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add an error as single field to the log entry. All it does is call
|
|
||||||
// `WithError` for the given `error`.
|
|
||||||
func (logger *Logger) WithError(err error) *Entry {
|
|
||||||
entry := logger.newEntry()
|
|
||||||
defer logger.releaseEntry(entry)
|
|
||||||
return entry.WithError(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a context to the log entry.
|
|
||||||
func (logger *Logger) WithContext(ctx context.Context) *Entry {
|
|
||||||
entry := logger.newEntry()
|
|
||||||
defer logger.releaseEntry(entry)
|
|
||||||
return entry.WithContext(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Overrides the time of the log entry.
|
|
||||||
func (logger *Logger) WithTime(t time.Time) *Entry {
|
|
||||||
entry := logger.newEntry()
|
|
||||||
defer logger.releaseEntry(entry)
|
|
||||||
return entry.WithTime(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Logf(level Level, format string, args ...interface{}) {
|
|
||||||
if logger.IsLevelEnabled(level) {
|
|
||||||
entry := logger.newEntry()
|
|
||||||
entry.Logf(level, format, args...)
|
|
||||||
logger.releaseEntry(entry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Tracef(format string, args ...interface{}) {
|
|
||||||
logger.Logf(TraceLevel, format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Debugf(format string, args ...interface{}) {
|
|
||||||
logger.Logf(DebugLevel, format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Infof(format string, args ...interface{}) {
|
|
||||||
logger.Logf(InfoLevel, format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Printf(format string, args ...interface{}) {
|
|
||||||
entry := logger.newEntry()
|
|
||||||
entry.Printf(format, args...)
|
|
||||||
logger.releaseEntry(entry)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Warnf(format string, args ...interface{}) {
|
|
||||||
logger.Logf(WarnLevel, format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Warningf(format string, args ...interface{}) {
|
|
||||||
logger.Warnf(format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Errorf(format string, args ...interface{}) {
|
|
||||||
logger.Logf(ErrorLevel, format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Fatalf(format string, args ...interface{}) {
|
|
||||||
logger.Logf(FatalLevel, format, args...)
|
|
||||||
logger.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Panicf(format string, args ...interface{}) {
|
|
||||||
logger.Logf(PanicLevel, format, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log will log a message at the level given as parameter.
|
|
||||||
// Warning: using Log at Panic or Fatal level will not respectively Panic nor Exit.
|
|
||||||
// For this behaviour Logger.Panic or Logger.Fatal should be used instead.
|
|
||||||
func (logger *Logger) Log(level Level, args ...interface{}) {
|
|
||||||
if logger.IsLevelEnabled(level) {
|
|
||||||
entry := logger.newEntry()
|
|
||||||
entry.Log(level, args...)
|
|
||||||
logger.releaseEntry(entry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) LogFn(level Level, fn LogFunction) {
|
|
||||||
if logger.IsLevelEnabled(level) {
|
|
||||||
entry := logger.newEntry()
|
|
||||||
entry.Log(level, fn()...)
|
|
||||||
logger.releaseEntry(entry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Trace(args ...interface{}) {
|
|
||||||
logger.Log(TraceLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Debug(args ...interface{}) {
|
|
||||||
logger.Log(DebugLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Info(args ...interface{}) {
|
|
||||||
logger.Log(InfoLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Print(args ...interface{}) {
|
|
||||||
entry := logger.newEntry()
|
|
||||||
entry.Print(args...)
|
|
||||||
logger.releaseEntry(entry)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Warn(args ...interface{}) {
|
|
||||||
logger.Log(WarnLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Warning(args ...interface{}) {
|
|
||||||
logger.Warn(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Error(args ...interface{}) {
|
|
||||||
logger.Log(ErrorLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Fatal(args ...interface{}) {
|
|
||||||
logger.Log(FatalLevel, args...)
|
|
||||||
logger.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Panic(args ...interface{}) {
|
|
||||||
logger.Log(PanicLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) TraceFn(fn LogFunction) {
|
|
||||||
logger.LogFn(TraceLevel, fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) DebugFn(fn LogFunction) {
|
|
||||||
logger.LogFn(DebugLevel, fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) InfoFn(fn LogFunction) {
|
|
||||||
logger.LogFn(InfoLevel, fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) PrintFn(fn LogFunction) {
|
|
||||||
entry := logger.newEntry()
|
|
||||||
entry.Print(fn()...)
|
|
||||||
logger.releaseEntry(entry)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) WarnFn(fn LogFunction) {
|
|
||||||
logger.LogFn(WarnLevel, fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) WarningFn(fn LogFunction) {
|
|
||||||
logger.WarnFn(fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) ErrorFn(fn LogFunction) {
|
|
||||||
logger.LogFn(ErrorLevel, fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) FatalFn(fn LogFunction) {
|
|
||||||
logger.LogFn(FatalLevel, fn)
|
|
||||||
logger.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) PanicFn(fn LogFunction) {
|
|
||||||
logger.LogFn(PanicLevel, fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Logln(level Level, args ...interface{}) {
|
|
||||||
if logger.IsLevelEnabled(level) {
|
|
||||||
entry := logger.newEntry()
|
|
||||||
entry.Logln(level, args...)
|
|
||||||
logger.releaseEntry(entry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Traceln(args ...interface{}) {
|
|
||||||
logger.Logln(TraceLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Debugln(args ...interface{}) {
|
|
||||||
logger.Logln(DebugLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Infoln(args ...interface{}) {
|
|
||||||
logger.Logln(InfoLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Println(args ...interface{}) {
|
|
||||||
entry := logger.newEntry()
|
|
||||||
entry.Println(args...)
|
|
||||||
logger.releaseEntry(entry)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Warnln(args ...interface{}) {
|
|
||||||
logger.Logln(WarnLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Warningln(args ...interface{}) {
|
|
||||||
logger.Warnln(args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Errorln(args ...interface{}) {
|
|
||||||
logger.Logln(ErrorLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Fatalln(args ...interface{}) {
|
|
||||||
logger.Logln(FatalLevel, args...)
|
|
||||||
logger.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Panicln(args ...interface{}) {
|
|
||||||
logger.Logln(PanicLevel, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) Exit(code int) {
|
|
||||||
runHandlers()
|
|
||||||
if logger.ExitFunc == nil {
|
|
||||||
logger.ExitFunc = os.Exit
|
|
||||||
}
|
|
||||||
logger.ExitFunc(code)
|
|
||||||
}
|
|
||||||
|
|
||||||
//When file is opened with appending mode, it's safe to
|
|
||||||
//write concurrently to a file (within 4k message on Linux).
|
|
||||||
//In these cases user can choose to disable the lock.
|
|
||||||
func (logger *Logger) SetNoLock() {
|
|
||||||
logger.mu.Disable()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) level() Level {
|
|
||||||
return Level(atomic.LoadUint32((*uint32)(&logger.Level)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetLevel sets the logger level.
|
|
||||||
func (logger *Logger) SetLevel(level Level) {
|
|
||||||
atomic.StoreUint32((*uint32)(&logger.Level), uint32(level))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetLevel returns the logger level.
|
|
||||||
func (logger *Logger) GetLevel() Level {
|
|
||||||
return logger.level()
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddHook adds a hook to the logger hooks.
|
|
||||||
func (logger *Logger) AddHook(hook Hook) {
|
|
||||||
logger.mu.Lock()
|
|
||||||
defer logger.mu.Unlock()
|
|
||||||
logger.Hooks.Add(hook)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsLevelEnabled checks if the log level of the logger is greater than the level param
|
|
||||||
func (logger *Logger) IsLevelEnabled(level Level) bool {
|
|
||||||
return logger.level() >= level
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetFormatter sets the logger formatter.
|
|
||||||
func (logger *Logger) SetFormatter(formatter Formatter) {
|
|
||||||
logger.mu.Lock()
|
|
||||||
defer logger.mu.Unlock()
|
|
||||||
logger.Formatter = formatter
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetOutput sets the logger output.
|
|
||||||
func (logger *Logger) SetOutput(output io.Writer) {
|
|
||||||
logger.mu.Lock()
|
|
||||||
defer logger.mu.Unlock()
|
|
||||||
logger.Out = output
|
|
||||||
}
|
|
||||||
|
|
||||||
func (logger *Logger) SetReportCaller(reportCaller bool) {
|
|
||||||
logger.mu.Lock()
|
|
||||||
defer logger.mu.Unlock()
|
|
||||||
logger.ReportCaller = reportCaller
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReplaceHooks replaces the logger hooks and returns the old ones
|
|
||||||
func (logger *Logger) ReplaceHooks(hooks LevelHooks) LevelHooks {
|
|
||||||
logger.mu.Lock()
|
|
||||||
oldHooks := logger.Hooks
|
|
||||||
logger.Hooks = hooks
|
|
||||||
logger.mu.Unlock()
|
|
||||||
return oldHooks
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetBufferPool sets the logger buffer pool.
|
|
||||||
func (logger *Logger) SetBufferPool(pool BufferPool) {
|
|
||||||
logger.mu.Lock()
|
|
||||||
defer logger.mu.Unlock()
|
|
||||||
logger.BufferPool = pool
|
|
||||||
}
|
|
||||||
186
vendor/github.com/sirupsen/logrus/logrus.go
generated
vendored
186
vendor/github.com/sirupsen/logrus/logrus.go
generated
vendored
@@ -1,186 +0,0 @@
|
|||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Fields type, used to pass to `WithFields`.
|
|
||||||
type Fields map[string]interface{}
|
|
||||||
|
|
||||||
// Level type
|
|
||||||
type Level uint32
|
|
||||||
|
|
||||||
// Convert the Level to a string. E.g. PanicLevel becomes "panic".
|
|
||||||
func (level Level) String() string {
|
|
||||||
if b, err := level.MarshalText(); err == nil {
|
|
||||||
return string(b)
|
|
||||||
} else {
|
|
||||||
return "unknown"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseLevel takes a string level and returns the Logrus log level constant.
|
|
||||||
func ParseLevel(lvl string) (Level, error) {
|
|
||||||
switch strings.ToLower(lvl) {
|
|
||||||
case "panic":
|
|
||||||
return PanicLevel, nil
|
|
||||||
case "fatal":
|
|
||||||
return FatalLevel, nil
|
|
||||||
case "error":
|
|
||||||
return ErrorLevel, nil
|
|
||||||
case "warn", "warning":
|
|
||||||
return WarnLevel, nil
|
|
||||||
case "info":
|
|
||||||
return InfoLevel, nil
|
|
||||||
case "debug":
|
|
||||||
return DebugLevel, nil
|
|
||||||
case "trace":
|
|
||||||
return TraceLevel, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var l Level
|
|
||||||
return l, fmt.Errorf("not a valid logrus Level: %q", lvl)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalText implements encoding.TextUnmarshaler.
|
|
||||||
func (level *Level) UnmarshalText(text []byte) error {
|
|
||||||
l, err := ParseLevel(string(text))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
*level = l
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (level Level) MarshalText() ([]byte, error) {
|
|
||||||
switch level {
|
|
||||||
case TraceLevel:
|
|
||||||
return []byte("trace"), nil
|
|
||||||
case DebugLevel:
|
|
||||||
return []byte("debug"), nil
|
|
||||||
case InfoLevel:
|
|
||||||
return []byte("info"), nil
|
|
||||||
case WarnLevel:
|
|
||||||
return []byte("warning"), nil
|
|
||||||
case ErrorLevel:
|
|
||||||
return []byte("error"), nil
|
|
||||||
case FatalLevel:
|
|
||||||
return []byte("fatal"), nil
|
|
||||||
case PanicLevel:
|
|
||||||
return []byte("panic"), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, fmt.Errorf("not a valid logrus level %d", level)
|
|
||||||
}
|
|
||||||
|
|
||||||
// A constant exposing all logging levels
|
|
||||||
var AllLevels = []Level{
|
|
||||||
PanicLevel,
|
|
||||||
FatalLevel,
|
|
||||||
ErrorLevel,
|
|
||||||
WarnLevel,
|
|
||||||
InfoLevel,
|
|
||||||
DebugLevel,
|
|
||||||
TraceLevel,
|
|
||||||
}
|
|
||||||
|
|
||||||
// These are the different logging levels. You can set the logging level to log
|
|
||||||
// on your instance of logger, obtained with `logrus.New()`.
|
|
||||||
const (
|
|
||||||
// PanicLevel level, highest level of severity. Logs and then calls panic with the
|
|
||||||
// message passed to Debug, Info, ...
|
|
||||||
PanicLevel Level = iota
|
|
||||||
// FatalLevel level. Logs and then calls `logger.Exit(1)`. It will exit even if the
|
|
||||||
// logging level is set to Panic.
|
|
||||||
FatalLevel
|
|
||||||
// ErrorLevel level. Logs. Used for errors that should definitely be noted.
|
|
||||||
// Commonly used for hooks to send errors to an error tracking service.
|
|
||||||
ErrorLevel
|
|
||||||
// WarnLevel level. Non-critical entries that deserve eyes.
|
|
||||||
WarnLevel
|
|
||||||
// InfoLevel level. General operational entries about what's going on inside the
|
|
||||||
// application.
|
|
||||||
InfoLevel
|
|
||||||
// DebugLevel level. Usually only enabled when debugging. Very verbose logging.
|
|
||||||
DebugLevel
|
|
||||||
// TraceLevel level. Designates finer-grained informational events than the Debug.
|
|
||||||
TraceLevel
|
|
||||||
)
|
|
||||||
|
|
||||||
// Won't compile if StdLogger can't be realized by a log.Logger
|
|
||||||
var (
|
|
||||||
_ StdLogger = &log.Logger{}
|
|
||||||
_ StdLogger = &Entry{}
|
|
||||||
_ StdLogger = &Logger{}
|
|
||||||
)
|
|
||||||
|
|
||||||
// StdLogger is what your logrus-enabled library should take, that way
|
|
||||||
// it'll accept a stdlib logger and a logrus logger. There's no standard
|
|
||||||
// interface, this is the closest we get, unfortunately.
|
|
||||||
type StdLogger interface {
|
|
||||||
Print(...interface{})
|
|
||||||
Printf(string, ...interface{})
|
|
||||||
Println(...interface{})
|
|
||||||
|
|
||||||
Fatal(...interface{})
|
|
||||||
Fatalf(string, ...interface{})
|
|
||||||
Fatalln(...interface{})
|
|
||||||
|
|
||||||
Panic(...interface{})
|
|
||||||
Panicf(string, ...interface{})
|
|
||||||
Panicln(...interface{})
|
|
||||||
}
|
|
||||||
|
|
||||||
// The FieldLogger interface generalizes the Entry and Logger types
|
|
||||||
type FieldLogger interface {
|
|
||||||
WithField(key string, value interface{}) *Entry
|
|
||||||
WithFields(fields Fields) *Entry
|
|
||||||
WithError(err error) *Entry
|
|
||||||
|
|
||||||
Debugf(format string, args ...interface{})
|
|
||||||
Infof(format string, args ...interface{})
|
|
||||||
Printf(format string, args ...interface{})
|
|
||||||
Warnf(format string, args ...interface{})
|
|
||||||
Warningf(format string, args ...interface{})
|
|
||||||
Errorf(format string, args ...interface{})
|
|
||||||
Fatalf(format string, args ...interface{})
|
|
||||||
Panicf(format string, args ...interface{})
|
|
||||||
|
|
||||||
Debug(args ...interface{})
|
|
||||||
Info(args ...interface{})
|
|
||||||
Print(args ...interface{})
|
|
||||||
Warn(args ...interface{})
|
|
||||||
Warning(args ...interface{})
|
|
||||||
Error(args ...interface{})
|
|
||||||
Fatal(args ...interface{})
|
|
||||||
Panic(args ...interface{})
|
|
||||||
|
|
||||||
Debugln(args ...interface{})
|
|
||||||
Infoln(args ...interface{})
|
|
||||||
Println(args ...interface{})
|
|
||||||
Warnln(args ...interface{})
|
|
||||||
Warningln(args ...interface{})
|
|
||||||
Errorln(args ...interface{})
|
|
||||||
Fatalln(args ...interface{})
|
|
||||||
Panicln(args ...interface{})
|
|
||||||
|
|
||||||
// IsDebugEnabled() bool
|
|
||||||
// IsInfoEnabled() bool
|
|
||||||
// IsWarnEnabled() bool
|
|
||||||
// IsErrorEnabled() bool
|
|
||||||
// IsFatalEnabled() bool
|
|
||||||
// IsPanicEnabled() bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ext1FieldLogger (the first extension to FieldLogger) is superfluous, it is
|
|
||||||
// here for consistancy. Do not use. Use Logger or Entry instead.
|
|
||||||
type Ext1FieldLogger interface {
|
|
||||||
FieldLogger
|
|
||||||
Tracef(format string, args ...interface{})
|
|
||||||
Trace(args ...interface{})
|
|
||||||
Traceln(args ...interface{})
|
|
||||||
}
|
|
||||||
11
vendor/github.com/sirupsen/logrus/terminal_check_appengine.go
generated
vendored
11
vendor/github.com/sirupsen/logrus/terminal_check_appengine.go
generated
vendored
@@ -1,11 +0,0 @@
|
|||||||
// +build appengine
|
|
||||||
|
|
||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
func checkIfTerminal(w io.Writer) bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
13
vendor/github.com/sirupsen/logrus/terminal_check_bsd.go
generated
vendored
13
vendor/github.com/sirupsen/logrus/terminal_check_bsd.go
generated
vendored
@@ -1,13 +0,0 @@
|
|||||||
// +build darwin dragonfly freebsd netbsd openbsd
|
|
||||||
// +build !js
|
|
||||||
|
|
||||||
package logrus
|
|
||||||
|
|
||||||
import "golang.org/x/sys/unix"
|
|
||||||
|
|
||||||
const ioctlReadTermios = unix.TIOCGETA
|
|
||||||
|
|
||||||
func isTerminal(fd int) bool {
|
|
||||||
_, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
|
|
||||||
return err == nil
|
|
||||||
}
|
|
||||||
7
vendor/github.com/sirupsen/logrus/terminal_check_js.go
generated
vendored
7
vendor/github.com/sirupsen/logrus/terminal_check_js.go
generated
vendored
@@ -1,7 +0,0 @@
|
|||||||
// +build js
|
|
||||||
|
|
||||||
package logrus
|
|
||||||
|
|
||||||
func isTerminal(fd int) bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
11
vendor/github.com/sirupsen/logrus/terminal_check_no_terminal.go
generated
vendored
11
vendor/github.com/sirupsen/logrus/terminal_check_no_terminal.go
generated
vendored
@@ -1,11 +0,0 @@
|
|||||||
// +build js nacl plan9
|
|
||||||
|
|
||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
func checkIfTerminal(w io.Writer) bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
17
vendor/github.com/sirupsen/logrus/terminal_check_notappengine.go
generated
vendored
17
vendor/github.com/sirupsen/logrus/terminal_check_notappengine.go
generated
vendored
@@ -1,17 +0,0 @@
|
|||||||
// +build !appengine,!js,!windows,!nacl,!plan9
|
|
||||||
|
|
||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
func checkIfTerminal(w io.Writer) bool {
|
|
||||||
switch v := w.(type) {
|
|
||||||
case *os.File:
|
|
||||||
return isTerminal(int(v.Fd()))
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
11
vendor/github.com/sirupsen/logrus/terminal_check_solaris.go
generated
vendored
11
vendor/github.com/sirupsen/logrus/terminal_check_solaris.go
generated
vendored
@@ -1,11 +0,0 @@
|
|||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
)
|
|
||||||
|
|
||||||
// IsTerminal returns true if the given file descriptor is a terminal.
|
|
||||||
func isTerminal(fd int) bool {
|
|
||||||
_, err := unix.IoctlGetTermio(fd, unix.TCGETA)
|
|
||||||
return err == nil
|
|
||||||
}
|
|
||||||
13
vendor/github.com/sirupsen/logrus/terminal_check_unix.go
generated
vendored
13
vendor/github.com/sirupsen/logrus/terminal_check_unix.go
generated
vendored
@@ -1,13 +0,0 @@
|
|||||||
// +build linux aix zos
|
|
||||||
// +build !js
|
|
||||||
|
|
||||||
package logrus
|
|
||||||
|
|
||||||
import "golang.org/x/sys/unix"
|
|
||||||
|
|
||||||
const ioctlReadTermios = unix.TCGETS
|
|
||||||
|
|
||||||
func isTerminal(fd int) bool {
|
|
||||||
_, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
|
|
||||||
return err == nil
|
|
||||||
}
|
|
||||||
27
vendor/github.com/sirupsen/logrus/terminal_check_windows.go
generated
vendored
27
vendor/github.com/sirupsen/logrus/terminal_check_windows.go
generated
vendored
@@ -1,27 +0,0 @@
|
|||||||
// +build !appengine,!js,windows
|
|
||||||
|
|
||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"golang.org/x/sys/windows"
|
|
||||||
)
|
|
||||||
|
|
||||||
func checkIfTerminal(w io.Writer) bool {
|
|
||||||
switch v := w.(type) {
|
|
||||||
case *os.File:
|
|
||||||
handle := windows.Handle(v.Fd())
|
|
||||||
var mode uint32
|
|
||||||
if err := windows.GetConsoleMode(handle, &mode); err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
mode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
|
||||||
if err := windows.SetConsoleMode(handle, mode); err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
339
vendor/github.com/sirupsen/logrus/text_formatter.go
generated
vendored
339
vendor/github.com/sirupsen/logrus/text_formatter.go
generated
vendored
@@ -1,339 +0,0 @@
|
|||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
"unicode/utf8"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
red = 31
|
|
||||||
yellow = 33
|
|
||||||
blue = 36
|
|
||||||
gray = 37
|
|
||||||
)
|
|
||||||
|
|
||||||
var baseTimestamp time.Time
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
baseTimestamp = time.Now()
|
|
||||||
}
|
|
||||||
|
|
||||||
// TextFormatter formats logs into text
|
|
||||||
type TextFormatter struct {
|
|
||||||
// Set to true to bypass checking for a TTY before outputting colors.
|
|
||||||
ForceColors bool
|
|
||||||
|
|
||||||
// Force disabling colors.
|
|
||||||
DisableColors bool
|
|
||||||
|
|
||||||
// Force quoting of all values
|
|
||||||
ForceQuote bool
|
|
||||||
|
|
||||||
// DisableQuote disables quoting for all values.
|
|
||||||
// DisableQuote will have a lower priority than ForceQuote.
|
|
||||||
// If both of them are set to true, quote will be forced on all values.
|
|
||||||
DisableQuote bool
|
|
||||||
|
|
||||||
// Override coloring based on CLICOLOR and CLICOLOR_FORCE. - https://bixense.com/clicolors/
|
|
||||||
EnvironmentOverrideColors bool
|
|
||||||
|
|
||||||
// Disable timestamp logging. useful when output is redirected to logging
|
|
||||||
// system that already adds timestamps.
|
|
||||||
DisableTimestamp bool
|
|
||||||
|
|
||||||
// Enable logging the full timestamp when a TTY is attached instead of just
|
|
||||||
// the time passed since beginning of execution.
|
|
||||||
FullTimestamp bool
|
|
||||||
|
|
||||||
// TimestampFormat to use for display when a full timestamp is printed.
|
|
||||||
// The format to use is the same than for time.Format or time.Parse from the standard
|
|
||||||
// library.
|
|
||||||
// The standard Library already provides a set of predefined format.
|
|
||||||
TimestampFormat string
|
|
||||||
|
|
||||||
// The fields are sorted by default for a consistent output. For applications
|
|
||||||
// that log extremely frequently and don't use the JSON formatter this may not
|
|
||||||
// be desired.
|
|
||||||
DisableSorting bool
|
|
||||||
|
|
||||||
// The keys sorting function, when uninitialized it uses sort.Strings.
|
|
||||||
SortingFunc func([]string)
|
|
||||||
|
|
||||||
// Disables the truncation of the level text to 4 characters.
|
|
||||||
DisableLevelTruncation bool
|
|
||||||
|
|
||||||
// PadLevelText Adds padding the level text so that all the levels output at the same length
|
|
||||||
// PadLevelText is a superset of the DisableLevelTruncation option
|
|
||||||
PadLevelText bool
|
|
||||||
|
|
||||||
// QuoteEmptyFields will wrap empty fields in quotes if true
|
|
||||||
QuoteEmptyFields bool
|
|
||||||
|
|
||||||
// Whether the logger's out is to a terminal
|
|
||||||
isTerminal bool
|
|
||||||
|
|
||||||
// FieldMap allows users to customize the names of keys for default fields.
|
|
||||||
// As an example:
|
|
||||||
// formatter := &TextFormatter{
|
|
||||||
// FieldMap: FieldMap{
|
|
||||||
// FieldKeyTime: "@timestamp",
|
|
||||||
// FieldKeyLevel: "@level",
|
|
||||||
// FieldKeyMsg: "@message"}}
|
|
||||||
FieldMap FieldMap
|
|
||||||
|
|
||||||
// CallerPrettyfier can be set by the user to modify the content
|
|
||||||
// of the function and file keys in the data when ReportCaller is
|
|
||||||
// activated. If any of the returned value is the empty string the
|
|
||||||
// corresponding key will be removed from fields.
|
|
||||||
CallerPrettyfier func(*runtime.Frame) (function string, file string)
|
|
||||||
|
|
||||||
terminalInitOnce sync.Once
|
|
||||||
|
|
||||||
// The max length of the level text, generated dynamically on init
|
|
||||||
levelTextMaxLength int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *TextFormatter) init(entry *Entry) {
|
|
||||||
if entry.Logger != nil {
|
|
||||||
f.isTerminal = checkIfTerminal(entry.Logger.Out)
|
|
||||||
}
|
|
||||||
// Get the max length of the level text
|
|
||||||
for _, level := range AllLevels {
|
|
||||||
levelTextLength := utf8.RuneCount([]byte(level.String()))
|
|
||||||
if levelTextLength > f.levelTextMaxLength {
|
|
||||||
f.levelTextMaxLength = levelTextLength
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *TextFormatter) isColored() bool {
|
|
||||||
isColored := f.ForceColors || (f.isTerminal && (runtime.GOOS != "windows"))
|
|
||||||
|
|
||||||
if f.EnvironmentOverrideColors {
|
|
||||||
switch force, ok := os.LookupEnv("CLICOLOR_FORCE"); {
|
|
||||||
case ok && force != "0":
|
|
||||||
isColored = true
|
|
||||||
case ok && force == "0", os.Getenv("CLICOLOR") == "0":
|
|
||||||
isColored = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return isColored && !f.DisableColors
|
|
||||||
}
|
|
||||||
|
|
||||||
// Format renders a single log entry
|
|
||||||
func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
|
|
||||||
data := make(Fields)
|
|
||||||
for k, v := range entry.Data {
|
|
||||||
data[k] = v
|
|
||||||
}
|
|
||||||
prefixFieldClashes(data, f.FieldMap, entry.HasCaller())
|
|
||||||
keys := make([]string, 0, len(data))
|
|
||||||
for k := range data {
|
|
||||||
keys = append(keys, k)
|
|
||||||
}
|
|
||||||
|
|
||||||
var funcVal, fileVal string
|
|
||||||
|
|
||||||
fixedKeys := make([]string, 0, 4+len(data))
|
|
||||||
if !f.DisableTimestamp {
|
|
||||||
fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyTime))
|
|
||||||
}
|
|
||||||
fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyLevel))
|
|
||||||
if entry.Message != "" {
|
|
||||||
fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyMsg))
|
|
||||||
}
|
|
||||||
if entry.err != "" {
|
|
||||||
fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyLogrusError))
|
|
||||||
}
|
|
||||||
if entry.HasCaller() {
|
|
||||||
if f.CallerPrettyfier != nil {
|
|
||||||
funcVal, fileVal = f.CallerPrettyfier(entry.Caller)
|
|
||||||
} else {
|
|
||||||
funcVal = entry.Caller.Function
|
|
||||||
fileVal = fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line)
|
|
||||||
}
|
|
||||||
|
|
||||||
if funcVal != "" {
|
|
||||||
fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyFunc))
|
|
||||||
}
|
|
||||||
if fileVal != "" {
|
|
||||||
fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyFile))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !f.DisableSorting {
|
|
||||||
if f.SortingFunc == nil {
|
|
||||||
sort.Strings(keys)
|
|
||||||
fixedKeys = append(fixedKeys, keys...)
|
|
||||||
} else {
|
|
||||||
if !f.isColored() {
|
|
||||||
fixedKeys = append(fixedKeys, keys...)
|
|
||||||
f.SortingFunc(fixedKeys)
|
|
||||||
} else {
|
|
||||||
f.SortingFunc(keys)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fixedKeys = append(fixedKeys, keys...)
|
|
||||||
}
|
|
||||||
|
|
||||||
var b *bytes.Buffer
|
|
||||||
if entry.Buffer != nil {
|
|
||||||
b = entry.Buffer
|
|
||||||
} else {
|
|
||||||
b = &bytes.Buffer{}
|
|
||||||
}
|
|
||||||
|
|
||||||
f.terminalInitOnce.Do(func() { f.init(entry) })
|
|
||||||
|
|
||||||
timestampFormat := f.TimestampFormat
|
|
||||||
if timestampFormat == "" {
|
|
||||||
timestampFormat = defaultTimestampFormat
|
|
||||||
}
|
|
||||||
if f.isColored() {
|
|
||||||
f.printColored(b, entry, keys, data, timestampFormat)
|
|
||||||
} else {
|
|
||||||
|
|
||||||
for _, key := range fixedKeys {
|
|
||||||
var value interface{}
|
|
||||||
switch {
|
|
||||||
case key == f.FieldMap.resolve(FieldKeyTime):
|
|
||||||
value = entry.Time.Format(timestampFormat)
|
|
||||||
case key == f.FieldMap.resolve(FieldKeyLevel):
|
|
||||||
value = entry.Level.String()
|
|
||||||
case key == f.FieldMap.resolve(FieldKeyMsg):
|
|
||||||
value = entry.Message
|
|
||||||
case key == f.FieldMap.resolve(FieldKeyLogrusError):
|
|
||||||
value = entry.err
|
|
||||||
case key == f.FieldMap.resolve(FieldKeyFunc) && entry.HasCaller():
|
|
||||||
value = funcVal
|
|
||||||
case key == f.FieldMap.resolve(FieldKeyFile) && entry.HasCaller():
|
|
||||||
value = fileVal
|
|
||||||
default:
|
|
||||||
value = data[key]
|
|
||||||
}
|
|
||||||
f.appendKeyValue(b, key, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
b.WriteByte('\n')
|
|
||||||
return b.Bytes(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, data Fields, timestampFormat string) {
|
|
||||||
var levelColor int
|
|
||||||
switch entry.Level {
|
|
||||||
case DebugLevel, TraceLevel:
|
|
||||||
levelColor = gray
|
|
||||||
case WarnLevel:
|
|
||||||
levelColor = yellow
|
|
||||||
case ErrorLevel, FatalLevel, PanicLevel:
|
|
||||||
levelColor = red
|
|
||||||
case InfoLevel:
|
|
||||||
levelColor = blue
|
|
||||||
default:
|
|
||||||
levelColor = blue
|
|
||||||
}
|
|
||||||
|
|
||||||
levelText := strings.ToUpper(entry.Level.String())
|
|
||||||
if !f.DisableLevelTruncation && !f.PadLevelText {
|
|
||||||
levelText = levelText[0:4]
|
|
||||||
}
|
|
||||||
if f.PadLevelText {
|
|
||||||
// Generates the format string used in the next line, for example "%-6s" or "%-7s".
|
|
||||||
// Based on the max level text length.
|
|
||||||
formatString := "%-" + strconv.Itoa(f.levelTextMaxLength) + "s"
|
|
||||||
// Formats the level text by appending spaces up to the max length, for example:
|
|
||||||
// - "INFO "
|
|
||||||
// - "WARNING"
|
|
||||||
levelText = fmt.Sprintf(formatString, levelText)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove a single newline if it already exists in the message to keep
|
|
||||||
// the behavior of logrus text_formatter the same as the stdlib log package
|
|
||||||
entry.Message = strings.TrimSuffix(entry.Message, "\n")
|
|
||||||
|
|
||||||
caller := ""
|
|
||||||
if entry.HasCaller() {
|
|
||||||
funcVal := fmt.Sprintf("%s()", entry.Caller.Function)
|
|
||||||
fileVal := fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line)
|
|
||||||
|
|
||||||
if f.CallerPrettyfier != nil {
|
|
||||||
funcVal, fileVal = f.CallerPrettyfier(entry.Caller)
|
|
||||||
}
|
|
||||||
|
|
||||||
if fileVal == "" {
|
|
||||||
caller = funcVal
|
|
||||||
} else if funcVal == "" {
|
|
||||||
caller = fileVal
|
|
||||||
} else {
|
|
||||||
caller = fileVal + " " + funcVal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case f.DisableTimestamp:
|
|
||||||
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m%s %-44s ", levelColor, levelText, caller, entry.Message)
|
|
||||||
case !f.FullTimestamp:
|
|
||||||
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d]%s %-44s ", levelColor, levelText, int(entry.Time.Sub(baseTimestamp)/time.Second), caller, entry.Message)
|
|
||||||
default:
|
|
||||||
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s]%s %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), caller, entry.Message)
|
|
||||||
}
|
|
||||||
for _, k := range keys {
|
|
||||||
v := data[k]
|
|
||||||
fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=", levelColor, k)
|
|
||||||
f.appendValue(b, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *TextFormatter) needsQuoting(text string) bool {
|
|
||||||
if f.ForceQuote {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if f.QuoteEmptyFields && len(text) == 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if f.DisableQuote {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, ch := range text {
|
|
||||||
if !((ch >= 'a' && ch <= 'z') ||
|
|
||||||
(ch >= 'A' && ch <= 'Z') ||
|
|
||||||
(ch >= '0' && ch <= '9') ||
|
|
||||||
ch == '-' || ch == '.' || ch == '_' || ch == '/' || ch == '@' || ch == '^' || ch == '+') {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) {
|
|
||||||
if b.Len() > 0 {
|
|
||||||
b.WriteByte(' ')
|
|
||||||
}
|
|
||||||
b.WriteString(key)
|
|
||||||
b.WriteByte('=')
|
|
||||||
f.appendValue(b, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) {
|
|
||||||
stringVal, ok := value.(string)
|
|
||||||
if !ok {
|
|
||||||
stringVal = fmt.Sprint(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !f.needsQuoting(stringVal) {
|
|
||||||
b.WriteString(stringVal)
|
|
||||||
} else {
|
|
||||||
b.WriteString(fmt.Sprintf("%q", stringVal))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
102
vendor/github.com/sirupsen/logrus/writer.go
generated
vendored
102
vendor/github.com/sirupsen/logrus/writer.go
generated
vendored
@@ -1,102 +0,0 @@
|
|||||||
package logrus
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"io"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Writer at INFO level. See WriterLevel for details.
|
|
||||||
func (logger *Logger) Writer() *io.PipeWriter {
|
|
||||||
return logger.WriterLevel(InfoLevel)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriterLevel returns an io.Writer that can be used to write arbitrary text to
|
|
||||||
// the logger at the given log level. Each line written to the writer will be
|
|
||||||
// printed in the usual way using formatters and hooks. The writer is part of an
|
|
||||||
// io.Pipe and it is the callers responsibility to close the writer when done.
|
|
||||||
// This can be used to override the standard library logger easily.
|
|
||||||
func (logger *Logger) WriterLevel(level Level) *io.PipeWriter {
|
|
||||||
return NewEntry(logger).WriterLevel(level)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Writer returns an io.Writer that writes to the logger at the info log level
|
|
||||||
func (entry *Entry) Writer() *io.PipeWriter {
|
|
||||||
return entry.WriterLevel(InfoLevel)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriterLevel returns an io.Writer that writes to the logger at the given log level
|
|
||||||
func (entry *Entry) WriterLevel(level Level) *io.PipeWriter {
|
|
||||||
reader, writer := io.Pipe()
|
|
||||||
|
|
||||||
var printFunc func(args ...interface{})
|
|
||||||
|
|
||||||
// Determine which log function to use based on the specified log level
|
|
||||||
switch level {
|
|
||||||
case TraceLevel:
|
|
||||||
printFunc = entry.Trace
|
|
||||||
case DebugLevel:
|
|
||||||
printFunc = entry.Debug
|
|
||||||
case InfoLevel:
|
|
||||||
printFunc = entry.Info
|
|
||||||
case WarnLevel:
|
|
||||||
printFunc = entry.Warn
|
|
||||||
case ErrorLevel:
|
|
||||||
printFunc = entry.Error
|
|
||||||
case FatalLevel:
|
|
||||||
printFunc = entry.Fatal
|
|
||||||
case PanicLevel:
|
|
||||||
printFunc = entry.Panic
|
|
||||||
default:
|
|
||||||
printFunc = entry.Print
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start a new goroutine to scan the input and write it to the logger using the specified print function.
|
|
||||||
// It splits the input into chunks of up to 64KB to avoid buffer overflows.
|
|
||||||
go entry.writerScanner(reader, printFunc)
|
|
||||||
|
|
||||||
// Set a finalizer function to close the writer when it is garbage collected
|
|
||||||
runtime.SetFinalizer(writer, writerFinalizer)
|
|
||||||
|
|
||||||
return writer
|
|
||||||
}
|
|
||||||
|
|
||||||
// writerScanner scans the input from the reader and writes it to the logger
|
|
||||||
func (entry *Entry) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) {
|
|
||||||
scanner := bufio.NewScanner(reader)
|
|
||||||
|
|
||||||
// Set the buffer size to the maximum token size to avoid buffer overflows
|
|
||||||
scanner.Buffer(make([]byte, bufio.MaxScanTokenSize), bufio.MaxScanTokenSize)
|
|
||||||
|
|
||||||
// Define a split function to split the input into chunks of up to 64KB
|
|
||||||
chunkSize := bufio.MaxScanTokenSize // 64KB
|
|
||||||
splitFunc := func(data []byte, atEOF bool) (int, []byte, error) {
|
|
||||||
if len(data) >= chunkSize {
|
|
||||||
return chunkSize, data[:chunkSize], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return bufio.ScanLines(data, atEOF)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use the custom split function to split the input
|
|
||||||
scanner.Split(splitFunc)
|
|
||||||
|
|
||||||
// Scan the input and write it to the logger using the specified print function
|
|
||||||
for scanner.Scan() {
|
|
||||||
printFunc(strings.TrimRight(scanner.Text(), "\r\n"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there was an error while scanning the input, log an error
|
|
||||||
if err := scanner.Err(); err != nil {
|
|
||||||
entry.Errorf("Error while reading from Writer: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close the reader when we are done
|
|
||||||
reader.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriterFinalizer is a finalizer function that closes then given writer when it is garbage collected
|
|
||||||
func writerFinalizer(writer *io.PipeWriter) {
|
|
||||||
writer.Close()
|
|
||||||
}
|
|
||||||
5
vendor/github.com/xmppo/go-xmpp/.travis.yml
generated
vendored
Normal file
5
vendor/github.com/xmppo/go-xmpp/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- tip
|
||||||
|
script:
|
||||||
|
- go test
|
||||||
4
vendor/golang.org/x/sys/LICENSE → vendor/github.com/xmppo/go-xmpp/LICENSE
generated
vendored
4
vendor/golang.org/x/sys/LICENSE → vendor/github.com/xmppo/go-xmpp/LICENSE
generated
vendored
@@ -1,4 +1,4 @@
|
|||||||
Copyright 2009 The Go Authors.
|
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
modification, are permitted provided that the following conditions are
|
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
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
in the documentation and/or other materials provided with the
|
in the documentation and/or other materials provided with the
|
||||||
distribution.
|
distribution.
|
||||||
* Neither the name of Google LLC nor the names of its
|
* Neither the name of Google Inc. nor the names of its
|
||||||
contributors may be used to endorse or promote products derived from
|
contributors may be used to endorse or promote products derived from
|
||||||
this software without specific prior written permission.
|
this software without specific prior written permission.
|
||||||
|
|
||||||
6
vendor/github.com/xmppo/go-xmpp/README.md
generated
vendored
Normal file
6
vendor/github.com/xmppo/go-xmpp/README.md
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
go-xmpp
|
||||||
|
=======
|
||||||
|
|
||||||
|
go xmpp library (original was written by russ cox )
|
||||||
|
|
||||||
|
[Documentation](https://godoc.org/github.com/xmppo/go-xmpp)
|
||||||
51
vendor/github.com/xmppo/go-xmpp/const.go
generated
vendored
Normal file
51
vendor/github.com/xmppo/go-xmpp/const.go
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package xmpp
|
||||||
|
|
||||||
|
const (
|
||||||
|
Version = "0.3.1"
|
||||||
|
// FAST Mechanisms
|
||||||
|
HT_SHA_256_ENDP = "HT-SHA-256-ENDP"
|
||||||
|
HT_SHA_256_EXPR = "HT-SHA-256-EXPR"
|
||||||
|
HT_SHA_256_NONE = "HT-SHA-256-NONE"
|
||||||
|
HT_SHA_256_UNIQ = "HT-SHA-256-UNIQ"
|
||||||
|
// IQ Types
|
||||||
|
IQTypeError = "error"
|
||||||
|
IQTypeGet = "get"
|
||||||
|
IQTypeResult = "result"
|
||||||
|
IQTypeSet = "set"
|
||||||
|
// SCRAM Mechanisms
|
||||||
|
SCRAM_SHA_1_PLUS = "SCRAM-SHA-1-PLUS"
|
||||||
|
SCRAM_SHA_1 = "SCRAM-SHA-1"
|
||||||
|
SCRAM_SHA_256_PLUS = "SCRAM-SHA-256-PLUS"
|
||||||
|
SCRAM_SHA_256 = "SCRAM-SHA-256"
|
||||||
|
SCRAM_SHA_512_PLUS = "SCRAM-SHA-512-PLUS"
|
||||||
|
SCRAM_SHA_512 = "SCRAM-SHA-512"
|
||||||
|
UPGR_SCRAM_SHA_256 = "UPGR-SCRAM-SHA-256"
|
||||||
|
UPGR_SCRAM_SHA_512 = "UPGR-SCRAM-SHA-512"
|
||||||
|
// XMPP Namespaces
|
||||||
|
XMPPNS_AVATAR_PEP_DATA = "urn:xmpp:avatar:data"
|
||||||
|
XMPPNS_AVATAR_PEP_METADATA = "urn:xmpp:avatar:metadata"
|
||||||
|
XMPPNS_BIND_0 = "urn:xmpp:bind:0"
|
||||||
|
XMPPNS_CLIENT = "jabber:client"
|
||||||
|
XMPPNS_DISCO_INFO = "http://jabber.org/protocol/disco#info"
|
||||||
|
XMPPNS_DISCO_ITEMS = "http://jabber.org/protocol/disco#items"
|
||||||
|
XMPPNS_FAST_0 = "urn:xmpp:fast:0"
|
||||||
|
XMPPNS_HTTP_UPLOAD_0 = "urn:xmpp:http:upload:0"
|
||||||
|
XMPPNS_IQ_VERSION = "jabber:iq:version"
|
||||||
|
XMPPNS_MUC = "http://jabber.org/protocol/muc"
|
||||||
|
XMPPNS_MUC_USER = "http://jabber.org/protocol/muc#user"
|
||||||
|
XMPPNS_PING = "urn:xmpp:ping"
|
||||||
|
XMPPNS_PUBSUB_EVENT = "http://jabber.org/protocol/pubsub#event"
|
||||||
|
XMPPNS_PUBSUB = "http://jabber.org/protocol/pubsub"
|
||||||
|
XMPPNS_SASL_2 = "urn:xmpp:sasl:2"
|
||||||
|
XMPPNS_SASL_CB_0 = "urn:xmpp:sasl-cb:0"
|
||||||
|
XMPPNS_SASL_UPGRADE_0 = "urn:xmpp:sasl:upgrade:0"
|
||||||
|
XMPPNS_XMPP_SASL = "urn:ietf:params:xml:ns:xmpp-sasl"
|
||||||
|
XMPPNS_SCRAM_UPGRADE_0 = "urn:xmpp:scram-upgrade:0"
|
||||||
|
XMPPNS_SID_0 = "urn:xmpp:sid:0"
|
||||||
|
XMPPNS_STREAM = "http://etherx.jabber.org/streams"
|
||||||
|
XMPPNS_STREAM_LIMITS_0 = "urn:xmpp:stream-limits:0"
|
||||||
|
XMPPNS_TIME = "urn:xmpp:time"
|
||||||
|
XMPPNS_XMPP_TLS = "urn:ietf:params:xml:ns:xmpp-tls"
|
||||||
|
XMPPNS_XMPP_BIND = "urn:ietf:params:xml:ns:xmpp-bind"
|
||||||
|
XMPPNS_XMPP_SESSION = "urn:ietf:params:xml:ns:xmpp-session"
|
||||||
|
)
|
||||||
2460
vendor/github.com/xmppo/go-xmpp/xmpp.go
generated
vendored
Normal file
2460
vendor/github.com/xmppo/go-xmpp/xmpp.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
|||||||
package xmpp
|
package xmpp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/sha1" //nolint:gosec // go fuck yourself, gosec. You don't even fucking read rfc. So shout the fuck up.
|
"crypto/sha1"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
@@ -9,11 +9,6 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
XMPPNS_AVATAR_PEP_DATA = "urn:xmpp:avatar:data" //nocritic:revive
|
|
||||||
XMPPNS_AVATAR_PEP_METADATA = "urn:xmpp:avatar:metadata" //nocritic:revive
|
|
||||||
)
|
|
||||||
|
|
||||||
type clientAvatarData struct {
|
type clientAvatarData struct {
|
||||||
XMLName xml.Name `xml:"data"`
|
XMLName xml.Name `xml:"data"`
|
||||||
Data []byte `xml:",innerxml"`
|
Data []byte `xml:",innerxml"`
|
||||||
@@ -52,8 +47,8 @@ type AvatarMetadata struct {
|
|||||||
|
|
||||||
func handleAvatarData(itemsBody []byte, from, id string) (AvatarData, error) {
|
func handleAvatarData(itemsBody []byte, from, id string) (AvatarData, error) {
|
||||||
var data clientAvatarData
|
var data clientAvatarData
|
||||||
|
err := xml.Unmarshal(itemsBody, &data)
|
||||||
if err := xml.Unmarshal(itemsBody, &data); err != nil {
|
if err != nil {
|
||||||
return AvatarData{}, err
|
return AvatarData{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,9 +59,8 @@ func handleAvatarData(itemsBody []byte, from, id string) (AvatarData, error) {
|
|||||||
return AvatarData{}, err
|
return AvatarData{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
hash := sha1.Sum(dataRaw) //nolint:gosec // go fuck yourself, gosec. This exact mechanics described in standard.
|
hash := sha1.Sum(dataRaw) //nolint: gosec,G401 // It is not about security.
|
||||||
hashStr := hex.EncodeToString(hash[:])
|
hashStr := hex.EncodeToString(hash[:])
|
||||||
|
|
||||||
if hashStr != id {
|
if hashStr != id {
|
||||||
return AvatarData{}, errors.New("SHA1 hashes do not match")
|
return AvatarData{}, errors.New("SHA1 hashes do not match")
|
||||||
}
|
}
|
||||||
@@ -78,12 +72,9 @@ func handleAvatarData(itemsBody []byte, from, id string) (AvatarData, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handleAvatarMetadata(body []byte, from string) (AvatarMetadata, error) {
|
func handleAvatarMetadata(body []byte, from string) (AvatarMetadata, error) {
|
||||||
var (
|
var meta clientAvatarMetadata
|
||||||
meta clientAvatarMetadata
|
err := xml.Unmarshal(body, &meta)
|
||||||
err error
|
if err != nil {
|
||||||
)
|
|
||||||
|
|
||||||
if err = xml.Unmarshal(body, &meta); err != nil {
|
|
||||||
return AvatarMetadata{}, err
|
return AvatarMetadata{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,10 +86,10 @@ func handleAvatarMetadata(body []byte, from string) (AvatarMetadata, error) {
|
|||||||
ID: meta.Info.ID,
|
ID: meta.Info.ID,
|
||||||
Type: meta.Info.Type,
|
Type: meta.Info.Type,
|
||||||
URL: meta.Info.URL,
|
URL: meta.Info.URL,
|
||||||
}, err
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// atoiw is a wrapper for atoi which just returns -1 if an error occurs.
|
// A wrapper for atoi which just returns -1 if an error occurs
|
||||||
func atoiw(str string) int {
|
func atoiw(str string) int {
|
||||||
i, err := strconv.Atoi(str)
|
i, err := strconv.Atoi(str)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -108,22 +99,22 @@ func atoiw(str string) int {
|
|||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) AvatarSubscribeMetadata(jid string) (string, error) {
|
func (c *Client) AvatarSubscribeMetadata(jid string) error {
|
||||||
return c.PubsubSubscribeNode(XMPPNS_AVATAR_PEP_METADATA, jid)
|
return c.PubsubSubscribeNode(XMPPNS_AVATAR_PEP_METADATA, jid)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) AvatarUnsubscribeMetadata(jid string) (string, error) {
|
func (c *Client) AvatarUnsubscribeMetadata(jid string) error {
|
||||||
return c.PubsubUnsubscribeNode(XMPPNS_AVATAR_PEP_METADATA, jid)
|
return c.PubsubUnsubscribeNode(XMPPNS_AVATAR_PEP_METADATA, jid)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) AvatarRequestData(jid string) (string, error) {
|
func (c *Client) AvatarRequestData(jid string) error {
|
||||||
return c.PubsubRequestLastItems(XMPPNS_AVATAR_PEP_DATA, jid)
|
return c.PubsubRequestLastItems(XMPPNS_AVATAR_PEP_DATA, jid)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) AvatarRequestDataByID(jid, id string) (string, error) {
|
func (c *Client) AvatarRequestDataByID(jid, id string) error {
|
||||||
return c.PubsubRequestItem(XMPPNS_AVATAR_PEP_DATA, jid, id)
|
return c.PubsubRequestItem(XMPPNS_AVATAR_PEP_DATA, jid, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) AvatarRequestMetadata(jid string) (string, error) {
|
func (c *Client) AvatarRequestMetadata(jid string) error {
|
||||||
return c.PubsubRequestLastItems(XMPPNS_AVATAR_PEP_METADATA, jid)
|
return c.PubsubRequestLastItems(XMPPNS_AVATAR_PEP_METADATA, jid)
|
||||||
}
|
}
|
||||||
@@ -4,11 +4,6 @@ import (
|
|||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
XMPPNS_DISCO_ITEMS = "http://jabber.org/protocol/disco#items" //nocritic:revive
|
|
||||||
XMPPNS_DISCO_INFO = "http://jabber.org/protocol/disco#info" //nocritic:revive
|
|
||||||
)
|
|
||||||
|
|
||||||
type clientDiscoFeature struct {
|
type clientDiscoFeature struct {
|
||||||
XMLName xml.Name `xml:"feature"`
|
XMLName xml.Name `xml:"feature"`
|
||||||
Var string `xml:"var,attr"`
|
Var string `xml:"var,attr"`
|
||||||
@@ -25,6 +20,7 @@ type clientDiscoQuery struct {
|
|||||||
XMLName xml.Name `xml:"query"`
|
XMLName xml.Name `xml:"query"`
|
||||||
Features []clientDiscoFeature `xml:"feature"`
|
Features []clientDiscoFeature `xml:"feature"`
|
||||||
Identities []clientDiscoIdentity `xml:"identity"`
|
Identities []clientDiscoIdentity `xml:"identity"`
|
||||||
|
X []DiscoX `xml:"x"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type clientDiscoItem struct {
|
type clientDiscoItem struct {
|
||||||
@@ -57,6 +53,18 @@ type DiscoResult struct {
|
|||||||
To string
|
To string
|
||||||
Features []string
|
Features []string
|
||||||
Identities []DiscoIdentity
|
Identities []DiscoIdentity
|
||||||
|
X []DiscoX
|
||||||
|
}
|
||||||
|
|
||||||
|
type DiscoX struct {
|
||||||
|
XMLName xml.Name `xml:"x"`
|
||||||
|
Field []DiscoXField `xml:"field"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DiscoXField struct {
|
||||||
|
Type string `xml:"type,attr"`
|
||||||
|
Var string `xml:"var,attr"`
|
||||||
|
Value []string `xml:"value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type DiscoItems struct {
|
type DiscoItems struct {
|
||||||
@@ -90,7 +98,6 @@ func clientIdentitiesToReturn(identities []clientDiscoIdentity) []DiscoIdentity
|
|||||||
|
|
||||||
func clientDiscoItemsToReturn(items []clientDiscoItem) []DiscoItem {
|
func clientDiscoItemsToReturn(items []clientDiscoItem) []DiscoItem {
|
||||||
var ret []DiscoItem
|
var ret []DiscoItem
|
||||||
|
|
||||||
for _, item := range items {
|
for _, item := range items {
|
||||||
ret = append(ret, DiscoItem{
|
ret = append(ret, DiscoItem{
|
||||||
Jid: item.Jid,
|
Jid: item.Jid,
|
||||||
@@ -4,23 +4,23 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrorServiceUnavailable implements error response about feature that is not available. Currently implemented for
|
// ErrorServiceUnavailable implements error response about a feature that is not available. Currently implemented for
|
||||||
// xep-0030.
|
// xep-0030.
|
||||||
// QueryXmlns is about incoming xmlns attribute in query tag.
|
// QueryXmlns is about incoming xmlns attribute in query tag.
|
||||||
// Node is about incoming node attribute in query tag (looks like it used only in disco#commands).
|
// Node is about incoming node attribute in query tag (looks like it used only in disco#commands).
|
||||||
//
|
//
|
||||||
// If queried feature is not here on purpose, standards suggest answer with this stanza.
|
// If queried feature is not here on purpose, standards suggest to answer with this stanza.
|
||||||
func (c *Client) ErrorServiceUnavailable(v IQ, queryXmlns, node string) (string, error) {
|
func (c *Client) ErrorServiceUnavailable(v IQ, queryXmlns, node string) (string, error) {
|
||||||
query := fmt.Sprintf("<query xmlns=\"%s\" ", queryXmlns)
|
query := fmt.Sprintf("<query xmlns='%s' ", queryXmlns)
|
||||||
|
|
||||||
if node != "" {
|
if node != "" {
|
||||||
query += fmt.Sprintf("node=\"%s\" />", node)
|
query += fmt.Sprintf("node='%s' />", node)
|
||||||
} else {
|
} else {
|
||||||
query += "/>"
|
query += "/>"
|
||||||
}
|
}
|
||||||
|
|
||||||
query += "<error type=\"cancel\">"
|
query += "<error type='cancel'>"
|
||||||
query += "<service-unavailable xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\" />"
|
query += "<service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas' />"
|
||||||
query += "</error>"
|
query += "</error>"
|
||||||
|
|
||||||
return c.RawInformation(
|
return c.RawInformation(
|
||||||
@@ -32,16 +32,16 @@ func (c *Client) ErrorServiceUnavailable(v IQ, queryXmlns, node string) (string,
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrorNotImplemented implements error response about feature that is not (yet?) implemented.
|
// ErrorNotImplemented implements error response about a feature that is not (yet?) implemented.
|
||||||
// Xmlns is about not implemented feature.
|
// Xmlns is about not implemented feature.
|
||||||
//
|
//
|
||||||
// If queried feature is not here because of it under development or for similar reasons, standards suggest answer with
|
// If queried feature is not here because of it under development or for similar reasons, standards suggest to answer with
|
||||||
// this stanza.
|
// this stanza.
|
||||||
func (c *Client) ErrorNotImplemented(v IQ, xmlns, feature string) (string, error) {
|
func (c *Client) ErrorNotImplemented(v IQ, xmlns, feature string) (string, error) {
|
||||||
query := "<error type=\"cancel\">"
|
query := "<error type='cancel'>"
|
||||||
query += "<feature-not-implemented xmlns=\"urn:ietf:params:xml:ns:xmpp-stanzas\" />"
|
query += "<feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas' />"
|
||||||
query += fmt.Sprintf(
|
query += fmt.Sprintf(
|
||||||
"<unsupported xmlns=\"%s\" feature=\"%s\" />",
|
"<unsupported xmlns='%s' feature='%s' />",
|
||||||
xmlns,
|
xmlns,
|
||||||
feature,
|
feature,
|
||||||
)
|
)
|
||||||
38
vendor/github.com/xmppo/go-xmpp/xmpp_http_upload.go
generated
vendored
Normal file
38
vendor/github.com/xmppo/go-xmpp/xmpp_http_upload.go
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
package xmpp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Slot struct {
|
||||||
|
// TODO: Maybe this doesn't belong here
|
||||||
|
ID string
|
||||||
|
XMLName xml.Name `xml:"slot"`
|
||||||
|
Put Put
|
||||||
|
Get Get
|
||||||
|
}
|
||||||
|
|
||||||
|
type Put struct {
|
||||||
|
XMLName xml.Name `xml:"put"`
|
||||||
|
Url string `xml:"url,attr"`
|
||||||
|
Headers []Header `xml:"header"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Get struct {
|
||||||
|
XMLName xml.Name `xml:"get"`
|
||||||
|
Url string `xml:"url,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Header struct {
|
||||||
|
XMLName xml.Name `xml:"header"`
|
||||||
|
Name string `xml:"name,attr"`
|
||||||
|
Value string `xml:",innerxml"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Oob is an out-of-band url/description, used in file uploads.
|
||||||
|
// See https://xmpp.org/extensions/xep-0066.html
|
||||||
|
type Oob struct {
|
||||||
|
XMLName xml.Name `xml:"x,xmlns:jabber:x:oob"`
|
||||||
|
Url string `xml:"url"`
|
||||||
|
Desc string `xml:"desc"`
|
||||||
|
}
|
||||||
97
vendor/github.com/xmppo/go-xmpp/xmpp_information_query.go
generated
vendored
Normal file
97
vendor/github.com/xmppo/go-xmpp/xmpp_information_query.go
generated
vendored
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
package xmpp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Client) Discovery() (string, error) {
|
||||||
|
// use UUIDv4 for a pseudo random id.
|
||||||
|
reqID := getUUID()
|
||||||
|
return c.RawInformationQuery(c.jid, c.domain, reqID, IQTypeGet, XMPPNS_DISCO_ITEMS, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discover information about a node. Empty node queries info about server itself.
|
||||||
|
func (c *Client) DiscoverNodeInfo(node string) (string, error) {
|
||||||
|
query := fmt.Sprintf("<query xmlns='%s' node='%s'/>", XMPPNS_DISCO_INFO, node)
|
||||||
|
return c.RawInformation(c.jid, c.domain, getUUID(), IQTypeGet, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discover information about given item from given jid.
|
||||||
|
func (c *Client) DiscoverInfo(to string) (string, error) {
|
||||||
|
query := fmt.Sprintf("<query xmlns='%s'/>", XMPPNS_DISCO_INFO)
|
||||||
|
return c.RawInformation(c.jid, to, getUUID(), IQTypeGet, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discover items that the server exposes
|
||||||
|
func (c *Client) DiscoverServerItems() (string, error) {
|
||||||
|
return c.DiscoverEntityItems(c.domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discover items that an entity exposes
|
||||||
|
func (c *Client) DiscoverEntityItems(jid string) (string, error) {
|
||||||
|
query := fmt.Sprintf("<query xmlns='%s'/>", XMPPNS_DISCO_ITEMS)
|
||||||
|
return c.RawInformation(c.jid, jid, getUUID(), IQTypeGet, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RawInformationQuery sends an information query request to the server.
|
||||||
|
func (c *Client) RawInformationQuery(from, to, id, iqType, requestNamespace, body string) (string, error) {
|
||||||
|
const xmlIQ = "<iq from='%s' to='%s' id='%s' type='%s'><query xmlns='%s'>%s</query></iq>\n"
|
||||||
|
_, err := fmt.Fprintf(c.stanzaWriter, xmlIQ, xmlEscape(from), xmlEscape(to), id, iqType, requestNamespace, body)
|
||||||
|
return id, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// rawInformation send a IQ request with the payload body to the server
|
||||||
|
func (c *Client) RawInformation(from, to, id, iqType, body string) (string, error) {
|
||||||
|
const xmlIQ = "<iq from='%s' to='%s' id='%s' type='%s'>%s</iq>\n"
|
||||||
|
_, err := fmt.Fprintf(c.stanzaWriter, xmlIQ, xmlEscape(from), xmlEscape(to), id, iqType, body)
|
||||||
|
return id, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// UrnXMPPTimeResponse implements response to query entity's current time (xep-0202).
|
||||||
|
func (c *Client) UrnXMPPTimeResponse(v IQ, timezoneOffset string) (string, error) {
|
||||||
|
query := fmt.Sprintf(
|
||||||
|
"<time xmlns=\"%s\"><tzo>%s</tzo><utc>%s</utc></time>",
|
||||||
|
XMPPNS_TIME,
|
||||||
|
timezoneOffset,
|
||||||
|
time.Now().UTC().Format(time.RFC3339),
|
||||||
|
)
|
||||||
|
|
||||||
|
return c.RawInformation(
|
||||||
|
v.To,
|
||||||
|
v.From,
|
||||||
|
v.ID,
|
||||||
|
IQTypeResult,
|
||||||
|
query,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IqVersionResponse responding with software version, according to xep-0092.
|
||||||
|
func (c *Client) IqVersionResponse(v IQ, name string, version string, os string) (string, error) {
|
||||||
|
if name == "" {
|
||||||
|
name = "go-xmpp"
|
||||||
|
version = Version
|
||||||
|
}
|
||||||
|
|
||||||
|
if version == "" {
|
||||||
|
version = "undefined"
|
||||||
|
}
|
||||||
|
|
||||||
|
query := fmt.Sprintf("<query xmlns=\"%s\">", XMPPNS_IQ_VERSION)
|
||||||
|
query += fmt.Sprintf("<name>%s</name>", name)
|
||||||
|
query += fmt.Sprintf("<version>%s</version>", version)
|
||||||
|
|
||||||
|
if os != "" {
|
||||||
|
query += fmt.Sprintf("<os>%s</os>", os)
|
||||||
|
}
|
||||||
|
|
||||||
|
query += "</query>"
|
||||||
|
|
||||||
|
return c.RawInformation(
|
||||||
|
v.To,
|
||||||
|
v.From,
|
||||||
|
v.ID,
|
||||||
|
IQTypeResult,
|
||||||
|
query,
|
||||||
|
)
|
||||||
|
}
|
||||||
133
vendor/github.com/xmppo/go-xmpp/xmpp_muc.go
generated
vendored
Normal file
133
vendor/github.com/xmppo/go-xmpp/xmpp_muc.go
generated
vendored
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
// Copyright 2013 Flo Lauber <dev@qatfy.at>. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// TODO(flo):
|
||||||
|
// - support password protected MUC rooms
|
||||||
|
// - cleanup signatures of join/leave functions
|
||||||
|
package xmpp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
NoHistory = 0
|
||||||
|
CharHistory = 1
|
||||||
|
StanzaHistory = 2
|
||||||
|
SecondsHistory = 3
|
||||||
|
SinceHistory = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
// Send sends room topic wrapped inside an XMPP message stanza body.
|
||||||
|
func (c *Client) SendTopic(chat Chat) (n int, err error) {
|
||||||
|
return fmt.Fprintf(c.stanzaWriter, "<message to='%s' type='%s' xml:lang='en'>"+"<subject>%s</subject></message>\n",
|
||||||
|
xmlEscape(chat.Remote), xmlEscape(chat.Type), xmlEscape(chat.Text))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) JoinMUCNoHistory(jid, nick string) (n int, err error) {
|
||||||
|
if nick == "" {
|
||||||
|
nick = c.jid
|
||||||
|
}
|
||||||
|
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
|
||||||
|
"<x xmlns='%s'>"+
|
||||||
|
"<history maxchars='0'/></x>"+
|
||||||
|
"</presence>\n",
|
||||||
|
xmlEscape(jid), xmlEscape(nick), XMPPNS_MUC)
|
||||||
|
}
|
||||||
|
|
||||||
|
// xep-0045 7.2
|
||||||
|
func (c *Client) JoinMUC(jid, nick string, history_type, history int, history_date *time.Time) (n int, err error) {
|
||||||
|
if nick == "" {
|
||||||
|
nick = c.jid
|
||||||
|
}
|
||||||
|
switch history_type {
|
||||||
|
case NoHistory:
|
||||||
|
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
|
||||||
|
"<x xmlns='%s' />"+
|
||||||
|
"</presence>\n",
|
||||||
|
xmlEscape(jid), xmlEscape(nick), XMPPNS_MUC)
|
||||||
|
case CharHistory:
|
||||||
|
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
|
||||||
|
"<x xmlns='%s'>"+
|
||||||
|
"<history maxchars='%d'/></x>"+
|
||||||
|
"</presence>\n",
|
||||||
|
xmlEscape(jid), xmlEscape(nick), XMPPNS_MUC, history)
|
||||||
|
case StanzaHistory:
|
||||||
|
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
|
||||||
|
"<x xmlns='%s'>"+
|
||||||
|
"<history maxstanzas='%d'/></x>"+
|
||||||
|
"</presence>\n",
|
||||||
|
xmlEscape(jid), xmlEscape(nick), XMPPNS_MUC, history)
|
||||||
|
case SecondsHistory:
|
||||||
|
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
|
||||||
|
"<x xmlns='%s'>"+
|
||||||
|
"<history seconds='%d'/></x>"+
|
||||||
|
"</presence>\n",
|
||||||
|
xmlEscape(jid), xmlEscape(nick), XMPPNS_MUC, history)
|
||||||
|
case SinceHistory:
|
||||||
|
if history_date != nil {
|
||||||
|
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
|
||||||
|
"<x xmlns='%s'>"+
|
||||||
|
"<history since='%s'/></x>"+
|
||||||
|
"</presence>\n",
|
||||||
|
xmlEscape(jid), xmlEscape(nick), XMPPNS_MUC, history_date.Format(time.RFC3339))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, errors.New("unknown history option")
|
||||||
|
}
|
||||||
|
|
||||||
|
// xep-0045 7.2.6
|
||||||
|
func (c *Client) JoinProtectedMUC(jid, nick string, password string, history_type, history int, history_date *time.Time) (n int, err error) {
|
||||||
|
if nick == "" {
|
||||||
|
nick = c.jid
|
||||||
|
}
|
||||||
|
switch history_type {
|
||||||
|
case NoHistory:
|
||||||
|
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
|
||||||
|
"<x xmlns='%s'>"+
|
||||||
|
"<password>%s</password>"+
|
||||||
|
"</x>"+
|
||||||
|
"</presence>\n",
|
||||||
|
xmlEscape(jid), xmlEscape(nick), XMPPNS_MUC, xmlEscape(password))
|
||||||
|
case CharHistory:
|
||||||
|
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
|
||||||
|
"<x xmlns='%s'>"+
|
||||||
|
"<password>%s</password>"+
|
||||||
|
"<history maxchars='%d'/></x>"+
|
||||||
|
"</presence>\n",
|
||||||
|
xmlEscape(jid), xmlEscape(nick), XMPPNS_MUC, xmlEscape(password), history)
|
||||||
|
case StanzaHistory:
|
||||||
|
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
|
||||||
|
"<x xmlns='%s'>"+
|
||||||
|
"<password>%s</password>"+
|
||||||
|
"<history maxstanzas='%d'/></x>"+
|
||||||
|
"</presence>\n",
|
||||||
|
xmlEscape(jid), xmlEscape(nick), XMPPNS_MUC, xmlEscape(password), history)
|
||||||
|
case SecondsHistory:
|
||||||
|
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
|
||||||
|
"<x xmlns='%s'>"+
|
||||||
|
"<password>%s</password>"+
|
||||||
|
"<history seconds='%d'/></x>"+
|
||||||
|
"</presence>\n",
|
||||||
|
xmlEscape(jid), xmlEscape(nick), XMPPNS_MUC, xmlEscape(password), history)
|
||||||
|
case SinceHistory:
|
||||||
|
if history_date != nil {
|
||||||
|
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
|
||||||
|
"<x xmlns='%s'>"+
|
||||||
|
"<password>%s</password>"+
|
||||||
|
"<history since='%s'/></x>"+
|
||||||
|
"</presence>\n",
|
||||||
|
xmlEscape(jid), xmlEscape(nick), XMPPNS_MUC, xmlEscape(password), history_date.Format(time.RFC3339))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, errors.New("unknown history option")
|
||||||
|
}
|
||||||
|
|
||||||
|
// xep-0045 7.14
|
||||||
|
func (c *Client) LeaveMUC(jid string) (n int, err error) {
|
||||||
|
return fmt.Fprintf(c.stanzaWriter, "<presence from='%s' to='%s' type='unavailable' />\n",
|
||||||
|
c.jid, xmlEscape(jid))
|
||||||
|
}
|
||||||
57
vendor/github.com/xmppo/go-xmpp/xmpp_ping.go
generated
vendored
Normal file
57
vendor/github.com/xmppo/go-xmpp/xmpp_ping.go
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
package xmpp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Client) PingC2S(jid, server string) error {
|
||||||
|
if jid == "" {
|
||||||
|
jid = c.jid
|
||||||
|
}
|
||||||
|
if server == "" {
|
||||||
|
server = c.domain
|
||||||
|
}
|
||||||
|
_, err := fmt.Fprintf(c.stanzaWriter, "<iq from='%s' to='%s' id='%s' type='get'>"+
|
||||||
|
"<ping xmlns='urn:xmpp:ping'/>"+
|
||||||
|
"</iq>\n",
|
||||||
|
xmlEscape(jid), xmlEscape(server), getUUID())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) PingS2S(fromServer, toServer string) error {
|
||||||
|
_, err := fmt.Fprintf(c.stanzaWriter, "<iq from='%s' to='%s' id='%s' type='get'>"+
|
||||||
|
"<ping xmlns='urn:xmpp:ping'/>"+
|
||||||
|
"</iq>\n",
|
||||||
|
xmlEscape(fromServer), xmlEscape(toServer), getUUID())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) SendResultPing(id, toServer string) error {
|
||||||
|
_, err := fmt.Fprintf(c.stanzaWriter, "<iq type='result' to='%s' id='%s'/>\n",
|
||||||
|
xmlEscape(toServer), xmlEscape(id))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) sendPeriodicPings() {
|
||||||
|
for range c.periodicPingTicker.C {
|
||||||
|
// Reset ticker for periodic pings if configured.
|
||||||
|
if c.periodicPings {
|
||||||
|
c.periodicPingTicker.Reset(c.periodicPingPeriod)
|
||||||
|
}
|
||||||
|
c.periodicPingID = getUUID()
|
||||||
|
c.periodicPingReply = false
|
||||||
|
_, err := fmt.Fprintf(c.stanzaWriter, "<iq from='%s' to='%s' id='%s' type='get'>"+
|
||||||
|
"<ping xmlns='urn:xmpp:ping'/></iq>\n",
|
||||||
|
xmlEscape(c.jid), xmlEscape(c.domain), c.periodicPingID)
|
||||||
|
if err != nil {
|
||||||
|
c.Close()
|
||||||
|
}
|
||||||
|
time.Sleep(c.periodicPingTimeout)
|
||||||
|
if !c.periodicPingReply {
|
||||||
|
c.shutdown = true
|
||||||
|
fmt.Fprintf(c.stanzaWriter, "</stream:stream>\n")
|
||||||
|
c.conn.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,11 +5,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
XMPPNS_PUBSUB = "http://jabber.org/protocol/pubsub" //nolint:revive
|
|
||||||
XMPPNS_PUBSUB_EVENT = "http://jabber.org/protocol/pubsub#event" //nolint:revive
|
|
||||||
)
|
|
||||||
|
|
||||||
type clientPubsubItem struct {
|
type clientPubsubItem struct {
|
||||||
XMLName xml.Name `xml:"item"`
|
XMLName xml.Name `xml:"item"`
|
||||||
ID string `xml:"id,attr"`
|
ID string `xml:"id,attr"`
|
||||||
@@ -22,11 +17,6 @@ type clientPubsubItems struct {
|
|||||||
Items []clientPubsubItem `xml:"item"`
|
Items []clientPubsubItem `xml:"item"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type clientPubsub struct {
|
|
||||||
XMLName xml.Name `xml:"pubsub"`
|
|
||||||
Items clientPubsubItems `xml:"items"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type clientPubsubEvent struct {
|
type clientPubsubEvent struct {
|
||||||
XMLName xml.Name `xml:"event"`
|
XMLName xml.Name `xml:"event"`
|
||||||
XMLNS string `xml:"xmlns,attr"`
|
XMLNS string `xml:"xmlns,attr"`
|
||||||
@@ -67,7 +57,7 @@ type PubsubItems struct {
|
|||||||
Items []PubsubItem
|
Items []PubsubItem
|
||||||
}
|
}
|
||||||
|
|
||||||
// Converts []clientPubsubItem to []PubsubItem.
|
// Converts []clientPubsubItem to []PubsubItem
|
||||||
func pubsubItemsToReturn(items []clientPubsubItem) []PubsubItem {
|
func pubsubItemsToReturn(items []clientPubsubItem) []PubsubItem {
|
||||||
var tmp []PubsubItem
|
var tmp []PubsubItem
|
||||||
for _, i := range items {
|
for _, i := range items {
|
||||||
@@ -96,7 +86,6 @@ func pubsubSubscriptionStanza(node, jid string) string {
|
|||||||
body := fmt.Sprintf("<subscribe node='%s' jid='%s'/>",
|
body := fmt.Sprintf("<subscribe node='%s' jid='%s'/>",
|
||||||
xmlEscape(node),
|
xmlEscape(node),
|
||||||
xmlEscape(jid))
|
xmlEscape(jid))
|
||||||
|
|
||||||
return pubsubStanza(body)
|
return pubsubStanza(body)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,34 +93,43 @@ func pubsubUnsubscriptionStanza(node, jid string) string {
|
|||||||
body := fmt.Sprintf("<unsubscribe node='%s' jid='%s'/>",
|
body := fmt.Sprintf("<unsubscribe node='%s' jid='%s'/>",
|
||||||
xmlEscape(node),
|
xmlEscape(node),
|
||||||
xmlEscape(jid))
|
xmlEscape(jid))
|
||||||
|
|
||||||
return pubsubStanza(body)
|
return pubsubStanza(body)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) PubsubSubscribeNode(node, jid string) (string, error) {
|
func (c *Client) PubsubSubscribeNode(node, jid string) error {
|
||||||
return c.RawInformation(c.jid,
|
id := getUUID()
|
||||||
|
c.subIDs = append(c.subIDs, id)
|
||||||
|
_, err := c.RawInformation(c.jid,
|
||||||
jid,
|
jid,
|
||||||
"sub1",
|
id,
|
||||||
"set",
|
"set",
|
||||||
pubsubSubscriptionStanza(node, c.jid))
|
pubsubSubscriptionStanza(node, c.jid))
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) PubsubUnsubscribeNode(node, jid string) (string, error) {
|
func (c *Client) PubsubUnsubscribeNode(node, jid string) error {
|
||||||
return c.RawInformation(c.jid,
|
id := getUUID()
|
||||||
|
c.unsubIDs = append(c.unsubIDs, id)
|
||||||
|
_, err := c.RawInformation(c.jid,
|
||||||
jid,
|
jid,
|
||||||
"unsub1",
|
id,
|
||||||
"set",
|
"set",
|
||||||
pubsubUnsubscriptionStanza(node, c.jid))
|
pubsubUnsubscriptionStanza(node, c.jid))
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) PubsubRequestLastItems(node, jid string) (string, error) {
|
func (c *Client) PubsubRequestLastItems(node, jid string) error {
|
||||||
|
id := getUUID()
|
||||||
|
c.itemsIDs = append(c.itemsIDs, id)
|
||||||
body := fmt.Sprintf("<items node='%s'/>", node)
|
body := fmt.Sprintf("<items node='%s'/>", node)
|
||||||
|
_, err := c.RawInformation(c.jid, jid, id, "get", pubsubStanza(body))
|
||||||
return c.RawInformation(c.jid, jid, "items1", "get", pubsubStanza(body))
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) PubsubRequestItem(node, jid, id string) (string, error) {
|
func (c *Client) PubsubRequestItem(node, jid, id string) error {
|
||||||
|
stanzaID := getUUID()
|
||||||
|
c.itemsIDs = append(c.itemsIDs, stanzaID)
|
||||||
body := fmt.Sprintf("<items node='%s'><item id='%s'/></items>", node, id)
|
body := fmt.Sprintf("<items node='%s'><item id='%s'/></items>", node, id)
|
||||||
|
_, err := c.RawInformation(c.jid, jid, stanzaID, "get", pubsubStanza(body))
|
||||||
return c.RawInformation(c.jid, jid, "items3", "get", pubsubStanza(body))
|
return err
|
||||||
}
|
}
|
||||||
30
vendor/github.com/xmppo/go-xmpp/xmpp_subscription.go
generated
vendored
Normal file
30
vendor/github.com/xmppo/go-xmpp/xmpp_subscription.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package xmpp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Client) ApproveSubscription(jid string) {
|
||||||
|
fmt.Fprintf(c.stanzaWriter, "<presence to='%s' type='subscribed'/>\n",
|
||||||
|
xmlEscape(jid))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) RevokeSubscription(jid string) {
|
||||||
|
fmt.Fprintf(c.stanzaWriter, "<presence to='%s' type='unsubscribed'/>\n",
|
||||||
|
xmlEscape(jid))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use RevertSubscription instead.
|
||||||
|
func (c *Client) RetrieveSubscription(jid string) {
|
||||||
|
c.RevertSubscription(jid)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) RevertSubscription(jid string) {
|
||||||
|
fmt.Fprintf(c.conn, "<presence to='%s' type='unsubscribe'/>\n",
|
||||||
|
xmlEscape(jid))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) RequestSubscription(jid string) {
|
||||||
|
fmt.Fprintf(c.stanzaWriter, "<presence to='%s' type='subscribe'/>\n",
|
||||||
|
xmlEscape(jid))
|
||||||
|
}
|
||||||
27
vendor/golang.org/x/crypto/LICENSE
generated
vendored
27
vendor/golang.org/x/crypto/LICENSE
generated
vendored
@@ -1,27 +0,0 @@
|
|||||||
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
|
|
||||||
met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following disclaimer
|
|
||||||
in the documentation and/or other materials provided with the
|
|
||||||
distribution.
|
|
||||||
* Neither the name of 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.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
22
vendor/golang.org/x/crypto/PATENTS
generated
vendored
22
vendor/golang.org/x/crypto/PATENTS
generated
vendored
@@ -1,22 +0,0 @@
|
|||||||
Additional IP Rights Grant (Patents)
|
|
||||||
|
|
||||||
"This implementation" means the copyrightable works distributed by
|
|
||||||
Google as part of the Go project.
|
|
||||||
|
|
||||||
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
|
||||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
|
||||||
patent license to make, have made, use, offer to sell, sell, import,
|
|
||||||
transfer and otherwise run, modify and propagate the contents of this
|
|
||||||
implementation of Go, where such license applies only to those patent
|
|
||||||
claims, both currently owned or controlled by Google and acquired in
|
|
||||||
the future, licensable by Google that are necessarily infringed by this
|
|
||||||
implementation of Go. This grant does not include claims that would be
|
|
||||||
infringed only as a consequence of further modification of this
|
|
||||||
implementation. If you or your agent or exclusive licensee institute or
|
|
||||||
order or agree to the institution of patent litigation against any
|
|
||||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
|
||||||
that this implementation of Go or any code incorporated within this
|
|
||||||
implementation of Go constitutes direct or contributory patent
|
|
||||||
infringement, or inducement of patent infringement, then any patent
|
|
||||||
rights granted to you under this License for this implementation of Go
|
|
||||||
shall terminate as of the date such litigation is filed.
|
|
||||||
77
vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go
generated
vendored
77
vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go
generated
vendored
@@ -1,77 +0,0 @@
|
|||||||
// Copyright 2012 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.
|
|
||||||
|
|
||||||
/*
|
|
||||||
Package pbkdf2 implements the key derivation function PBKDF2 as defined in RFC
|
|
||||||
2898 / PKCS #5 v2.0.
|
|
||||||
|
|
||||||
A key derivation function is useful when encrypting data based on a password
|
|
||||||
or any other not-fully-random data. It uses a pseudorandom function to derive
|
|
||||||
a secure encryption key based on the password.
|
|
||||||
|
|
||||||
While v2.0 of the standard defines only one pseudorandom function to use,
|
|
||||||
HMAC-SHA1, the drafted v2.1 specification allows use of all five FIPS Approved
|
|
||||||
Hash Functions SHA-1, SHA-224, SHA-256, SHA-384 and SHA-512 for HMAC. To
|
|
||||||
choose, you can pass the `New` functions from the different SHA packages to
|
|
||||||
pbkdf2.Key.
|
|
||||||
*/
|
|
||||||
package pbkdf2
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/hmac"
|
|
||||||
"hash"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Key derives a key from the password, salt and iteration count, returning a
|
|
||||||
// []byte of length keylen that can be used as cryptographic key. The key is
|
|
||||||
// derived based on the method described as PBKDF2 with the HMAC variant using
|
|
||||||
// the supplied hash function.
|
|
||||||
//
|
|
||||||
// For example, to use a HMAC-SHA-1 based PBKDF2 key derivation function, you
|
|
||||||
// can get a derived key for e.g. AES-256 (which needs a 32-byte key) by
|
|
||||||
// doing:
|
|
||||||
//
|
|
||||||
// dk := pbkdf2.Key([]byte("some password"), salt, 4096, 32, sha1.New)
|
|
||||||
//
|
|
||||||
// Remember to get a good random salt. At least 8 bytes is recommended by the
|
|
||||||
// RFC.
|
|
||||||
//
|
|
||||||
// Using a higher iteration count will increase the cost of an exhaustive
|
|
||||||
// search but will also make derivation proportionally slower.
|
|
||||||
func Key(password, salt []byte, iter, keyLen int, h func() hash.Hash) []byte {
|
|
||||||
prf := hmac.New(h, password)
|
|
||||||
hashLen := prf.Size()
|
|
||||||
numBlocks := (keyLen + hashLen - 1) / hashLen
|
|
||||||
|
|
||||||
var buf [4]byte
|
|
||||||
dk := make([]byte, 0, numBlocks*hashLen)
|
|
||||||
U := make([]byte, hashLen)
|
|
||||||
for block := 1; block <= numBlocks; block++ {
|
|
||||||
// N.B.: || means concatenation, ^ means XOR
|
|
||||||
// for each block T_i = U_1 ^ U_2 ^ ... ^ U_iter
|
|
||||||
// U_1 = PRF(password, salt || uint(i))
|
|
||||||
prf.Reset()
|
|
||||||
prf.Write(salt)
|
|
||||||
buf[0] = byte(block >> 24)
|
|
||||||
buf[1] = byte(block >> 16)
|
|
||||||
buf[2] = byte(block >> 8)
|
|
||||||
buf[3] = byte(block)
|
|
||||||
prf.Write(buf[:4])
|
|
||||||
dk = prf.Sum(dk)
|
|
||||||
T := dk[len(dk)-hashLen:]
|
|
||||||
copy(U, T)
|
|
||||||
|
|
||||||
// U_n = PRF(password, U_(n-1))
|
|
||||||
for n := 2; n <= iter; n++ {
|
|
||||||
prf.Reset()
|
|
||||||
prf.Write(U)
|
|
||||||
U = U[:0]
|
|
||||||
U = prf.Sum(U)
|
|
||||||
for x := range U {
|
|
||||||
T[x] ^= U[x]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return dk[:keyLen]
|
|
||||||
}
|
|
||||||
27
vendor/golang.org/x/exp/LICENSE
generated
vendored
27
vendor/golang.org/x/exp/LICENSE
generated
vendored
@@ -1,27 +0,0 @@
|
|||||||
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
|
|
||||||
met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following disclaimer
|
|
||||||
in the documentation and/or other materials provided with the
|
|
||||||
distribution.
|
|
||||||
* Neither the name of 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.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
22
vendor/golang.org/x/exp/PATENTS
generated
vendored
22
vendor/golang.org/x/exp/PATENTS
generated
vendored
@@ -1,22 +0,0 @@
|
|||||||
Additional IP Rights Grant (Patents)
|
|
||||||
|
|
||||||
"This implementation" means the copyrightable works distributed by
|
|
||||||
Google as part of the Go project.
|
|
||||||
|
|
||||||
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
|
||||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
|
||||||
patent license to make, have made, use, offer to sell, sell, import,
|
|
||||||
transfer and otherwise run, modify and propagate the contents of this
|
|
||||||
implementation of Go, where such license applies only to those patent
|
|
||||||
claims, both currently owned or controlled by Google and acquired in
|
|
||||||
the future, licensable by Google that are necessarily infringed by this
|
|
||||||
implementation of Go. This grant does not include claims that would be
|
|
||||||
infringed only as a consequence of further modification of this
|
|
||||||
implementation. If you or your agent or exclusive licensee institute or
|
|
||||||
order or agree to the institution of patent litigation against any
|
|
||||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
|
||||||
that this implementation of Go or any code incorporated within this
|
|
||||||
implementation of Go constitutes direct or contributory patent
|
|
||||||
infringement, or inducement of patent infringement, then any patent
|
|
||||||
rights granted to you under this License for this implementation of Go
|
|
||||||
shall terminate as of the date such litigation is filed.
|
|
||||||
182
vendor/golang.org/x/exp/slices/slices.go
generated
vendored
182
vendor/golang.org/x/exp/slices/slices.go
generated
vendored
@@ -1,182 +0,0 @@
|
|||||||
// Copyright 2021 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.
|
|
||||||
|
|
||||||
// Package slices defines various functions useful with slices of any type.
|
|
||||||
package slices
|
|
||||||
|
|
||||||
import (
|
|
||||||
"cmp"
|
|
||||||
"slices"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Equal reports whether two slices are equal: the same length and all
|
|
||||||
// elements equal. If the lengths are different, Equal returns false.
|
|
||||||
// Otherwise, the elements are compared in increasing index order, and the
|
|
||||||
// comparison stops at the first unequal pair.
|
|
||||||
// Floating point NaNs are not considered equal.
|
|
||||||
//
|
|
||||||
//go:fix inline
|
|
||||||
func Equal[S ~[]E, E comparable](s1, s2 S) bool {
|
|
||||||
return slices.Equal(s1, s2)
|
|
||||||
}
|
|
||||||
|
|
||||||
// EqualFunc reports whether two slices are equal using an equality
|
|
||||||
// function on each pair of elements. If the lengths are different,
|
|
||||||
// EqualFunc returns false. Otherwise, the elements are compared in
|
|
||||||
// increasing index order, and the comparison stops at the first index
|
|
||||||
// for which eq returns false.
|
|
||||||
//
|
|
||||||
//go:fix inline
|
|
||||||
func EqualFunc[S1 ~[]E1, S2 ~[]E2, E1, E2 any](s1 S1, s2 S2, eq func(E1, E2) bool) bool {
|
|
||||||
return slices.EqualFunc(s1, s2, eq)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compare compares the elements of s1 and s2, using [cmp.Compare] on each pair
|
|
||||||
// of elements. The elements are compared sequentially, starting at index 0,
|
|
||||||
// until one element is not equal to the other.
|
|
||||||
// The result of comparing the first non-matching elements is returned.
|
|
||||||
// If both slices are equal until one of them ends, the shorter slice is
|
|
||||||
// considered less than the longer one.
|
|
||||||
// The result is 0 if s1 == s2, -1 if s1 < s2, and +1 if s1 > s2.
|
|
||||||
//
|
|
||||||
//go:fix inline
|
|
||||||
func Compare[S ~[]E, E cmp.Ordered](s1, s2 S) int {
|
|
||||||
return slices.Compare(s1, s2)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CompareFunc is like [Compare] but uses a custom comparison function on each
|
|
||||||
// pair of elements.
|
|
||||||
// The result is the first non-zero result of cmp; if cmp always
|
|
||||||
// returns 0 the result is 0 if len(s1) == len(s2), -1 if len(s1) < len(s2),
|
|
||||||
// and +1 if len(s1) > len(s2).
|
|
||||||
//
|
|
||||||
//go:fix inline
|
|
||||||
func CompareFunc[S1 ~[]E1, S2 ~[]E2, E1, E2 any](s1 S1, s2 S2, cmp func(E1, E2) int) int {
|
|
||||||
return slices.CompareFunc(s1, s2, cmp)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Index returns the index of the first occurrence of v in s,
|
|
||||||
// or -1 if not present.
|
|
||||||
//
|
|
||||||
//go:fix inline
|
|
||||||
func Index[S ~[]E, E comparable](s S, v E) int {
|
|
||||||
return slices.Index(s, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IndexFunc returns the first index i satisfying f(s[i]),
|
|
||||||
// or -1 if none do.
|
|
||||||
//
|
|
||||||
//go:fix inline
|
|
||||||
func IndexFunc[S ~[]E, E any](s S, f func(E) bool) int {
|
|
||||||
return slices.IndexFunc(s, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Contains reports whether v is present in s.
|
|
||||||
//
|
|
||||||
//go:fix inline
|
|
||||||
func Contains[S ~[]E, E comparable](s S, v E) bool {
|
|
||||||
return slices.Contains(s, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainsFunc reports whether at least one
|
|
||||||
// element e of s satisfies f(e).
|
|
||||||
//
|
|
||||||
//go:fix inline
|
|
||||||
func ContainsFunc[S ~[]E, E any](s S, f func(E) bool) bool {
|
|
||||||
return slices.ContainsFunc(s, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert inserts the values v... into s at index i,
|
|
||||||
// returning the modified slice.
|
|
||||||
// The elements at s[i:] are shifted up to make room.
|
|
||||||
// In the returned slice r, r[i] == v[0],
|
|
||||||
// and r[i+len(v)] == value originally at r[i].
|
|
||||||
// Insert panics if i is out of range.
|
|
||||||
// This function is O(len(s) + len(v)).
|
|
||||||
//
|
|
||||||
//go:fix inline
|
|
||||||
func Insert[S ~[]E, E any](s S, i int, v ...E) S {
|
|
||||||
return slices.Insert(s, i, v...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete removes the elements s[i:j] from s, returning the modified slice.
|
|
||||||
// Delete panics if j > len(s) or s[i:j] is not a valid slice of s.
|
|
||||||
// Delete is O(len(s)-i), so if many items must be deleted, it is better to
|
|
||||||
// make a single call deleting them all together than to delete one at a time.
|
|
||||||
// Delete zeroes the elements s[len(s)-(j-i):len(s)].
|
|
||||||
//
|
|
||||||
//go:fix inline
|
|
||||||
func Delete[S ~[]E, E any](s S, i, j int) S {
|
|
||||||
return slices.Delete(s, i, j)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteFunc removes any elements from s for which del returns true,
|
|
||||||
// returning the modified slice.
|
|
||||||
// DeleteFunc zeroes the elements between the new length and the original length.
|
|
||||||
//
|
|
||||||
//go:fix inline
|
|
||||||
func DeleteFunc[S ~[]E, E any](s S, del func(E) bool) S {
|
|
||||||
return slices.DeleteFunc(s, del)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace replaces the elements s[i:j] by the given v, and returns the
|
|
||||||
// modified slice. Replace panics if s[i:j] is not a valid slice of s.
|
|
||||||
// When len(v) < (j-i), Replace zeroes the elements between the new length and the original length.
|
|
||||||
//
|
|
||||||
//go:fix inline
|
|
||||||
func Replace[S ~[]E, E any](s S, i, j int, v ...E) S {
|
|
||||||
return slices.Replace(s, i, j, v...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clone returns a copy of the slice.
|
|
||||||
// The elements are copied using assignment, so this is a shallow clone.
|
|
||||||
//
|
|
||||||
//go:fix inline
|
|
||||||
func Clone[S ~[]E, E any](s S) S {
|
|
||||||
return slices.Clone(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compact replaces consecutive runs of equal elements with a single copy.
|
|
||||||
// This is like the uniq command found on Unix.
|
|
||||||
// Compact modifies the contents of the slice s and returns the modified slice,
|
|
||||||
// which may have a smaller length.
|
|
||||||
// Compact zeroes the elements between the new length and the original length.
|
|
||||||
//
|
|
||||||
//go:fix inline
|
|
||||||
func Compact[S ~[]E, E comparable](s S) S {
|
|
||||||
return slices.Compact(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CompactFunc is like [Compact] but uses an equality function to compare elements.
|
|
||||||
// For runs of elements that compare equal, CompactFunc keeps the first one.
|
|
||||||
// CompactFunc zeroes the elements between the new length and the original length.
|
|
||||||
//
|
|
||||||
//go:fix inline
|
|
||||||
func CompactFunc[S ~[]E, E any](s S, eq func(E, E) bool) S {
|
|
||||||
return slices.CompactFunc(s, eq)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Grow increases the slice's capacity, if necessary, to guarantee space for
|
|
||||||
// another n elements. After Grow(n), at least n elements can be appended
|
|
||||||
// to the slice without another allocation. If n is negative or too large to
|
|
||||||
// allocate the memory, Grow panics.
|
|
||||||
//
|
|
||||||
//go:fix inline
|
|
||||||
func Grow[S ~[]E, E any](s S, n int) S {
|
|
||||||
return slices.Grow(s, n)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clip removes unused capacity from the slice, returning s[:len(s):len(s)].
|
|
||||||
//
|
|
||||||
//go:fix inline
|
|
||||||
func Clip[S ~[]E, E any](s S) S {
|
|
||||||
return slices.Clip(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reverse reverses the elements of the slice in place.
|
|
||||||
//
|
|
||||||
//go:fix inline
|
|
||||||
func Reverse[S ~[]E, E any](s S) {
|
|
||||||
slices.Reverse(s)
|
|
||||||
}
|
|
||||||
115
vendor/golang.org/x/exp/slices/sort.go
generated
vendored
115
vendor/golang.org/x/exp/slices/sort.go
generated
vendored
@@ -1,115 +0,0 @@
|
|||||||
// Copyright 2022 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.
|
|
||||||
|
|
||||||
package slices
|
|
||||||
|
|
||||||
import (
|
|
||||||
"cmp"
|
|
||||||
"slices"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Sort sorts a slice of any ordered type in ascending order.
|
|
||||||
// When sorting floating-point numbers, NaNs are ordered before other values.
|
|
||||||
//
|
|
||||||
//go:fix inline
|
|
||||||
func Sort[S ~[]E, E cmp.Ordered](x S) {
|
|
||||||
slices.Sort(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SortFunc sorts the slice x in ascending order as determined by the cmp
|
|
||||||
// function. This sort is not guaranteed to be stable.
|
|
||||||
// cmp(a, b) should return a negative number when a < b, a positive number when
|
|
||||||
// a > b and zero when a == b or when a is not comparable to b in the sense
|
|
||||||
// of the formal definition of Strict Weak Ordering.
|
|
||||||
//
|
|
||||||
// SortFunc requires that cmp is a strict weak ordering.
|
|
||||||
// See https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings.
|
|
||||||
// To indicate 'uncomparable', return 0 from the function.
|
|
||||||
//
|
|
||||||
//go:fix inline
|
|
||||||
func SortFunc[S ~[]E, E any](x S, cmp func(a, b E) int) {
|
|
||||||
slices.SortFunc(x, cmp)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SortStableFunc sorts the slice x while keeping the original order of equal
|
|
||||||
// elements, using cmp to compare elements in the same way as [SortFunc].
|
|
||||||
//
|
|
||||||
//go:fix inline
|
|
||||||
func SortStableFunc[S ~[]E, E any](x S, cmp func(a, b E) int) {
|
|
||||||
slices.SortStableFunc(x, cmp)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsSorted reports whether x is sorted in ascending order.
|
|
||||||
//
|
|
||||||
//go:fix inline
|
|
||||||
func IsSorted[S ~[]E, E cmp.Ordered](x S) bool {
|
|
||||||
return slices.IsSorted(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsSortedFunc reports whether x is sorted in ascending order, with cmp as the
|
|
||||||
// comparison function as defined by [SortFunc].
|
|
||||||
//
|
|
||||||
//go:fix inline
|
|
||||||
func IsSortedFunc[S ~[]E, E any](x S, cmp func(a, b E) int) bool {
|
|
||||||
return slices.IsSortedFunc(x, cmp)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Min returns the minimal value in x. It panics if x is empty.
|
|
||||||
// For floating-point numbers, Min propagates NaNs (any NaN value in x
|
|
||||||
// forces the output to be NaN).
|
|
||||||
//
|
|
||||||
//go:fix inline
|
|
||||||
func Min[S ~[]E, E cmp.Ordered](x S) E {
|
|
||||||
return slices.Min(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MinFunc returns the minimal value in x, using cmp to compare elements.
|
|
||||||
// It panics if x is empty. If there is more than one minimal element
|
|
||||||
// according to the cmp function, MinFunc returns the first one.
|
|
||||||
//
|
|
||||||
//go:fix inline
|
|
||||||
func MinFunc[S ~[]E, E any](x S, cmp func(a, b E) int) E {
|
|
||||||
return slices.MinFunc(x, cmp)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Max returns the maximal value in x. It panics if x is empty.
|
|
||||||
// For floating-point E, Max propagates NaNs (any NaN value in x
|
|
||||||
// forces the output to be NaN).
|
|
||||||
//
|
|
||||||
//go:fix inline
|
|
||||||
func Max[S ~[]E, E cmp.Ordered](x S) E {
|
|
||||||
return slices.Max(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MaxFunc returns the maximal value in x, using cmp to compare elements.
|
|
||||||
// It panics if x is empty. If there is more than one maximal element
|
|
||||||
// according to the cmp function, MaxFunc returns the first one.
|
|
||||||
//
|
|
||||||
//go:fix inline
|
|
||||||
func MaxFunc[S ~[]E, E any](x S, cmp func(a, b E) int) E {
|
|
||||||
return slices.MaxFunc(x, cmp)
|
|
||||||
}
|
|
||||||
|
|
||||||
// BinarySearch searches for target in a sorted slice and returns the position
|
|
||||||
// where target is found, or the position where target would appear in the
|
|
||||||
// sort order; it also returns a bool saying whether the target is really found
|
|
||||||
// in the slice. The slice must be sorted in increasing order.
|
|
||||||
//
|
|
||||||
//go:fix inline
|
|
||||||
func BinarySearch[S ~[]E, E cmp.Ordered](x S, target E) (int, bool) {
|
|
||||||
return slices.BinarySearch(x, target)
|
|
||||||
}
|
|
||||||
|
|
||||||
// BinarySearchFunc works like [BinarySearch], but uses a custom comparison
|
|
||||||
// function. The slice must be sorted in increasing order, where "increasing"
|
|
||||||
// is defined by cmp. cmp should return 0 if the slice element matches
|
|
||||||
// the target, a negative number if the slice element precedes the target,
|
|
||||||
// or a positive number if the slice element follows the target.
|
|
||||||
// cmp must implement the same ordering as the slice, such that if
|
|
||||||
// cmp(a, t) < 0 and cmp(b, t) >= 0, then a must precede b in the slice.
|
|
||||||
//
|
|
||||||
//go:fix inline
|
|
||||||
func BinarySearchFunc[S ~[]E, E, T any](x S, target T, cmp func(E, T) int) (int, bool) {
|
|
||||||
return slices.BinarySearchFunc(x, target, cmp)
|
|
||||||
}
|
|
||||||
70
vendor/golang.org/x/net/context/context.go
generated
vendored
70
vendor/golang.org/x/net/context/context.go
generated
vendored
@@ -2,44 +2,9 @@
|
|||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// Package context defines the Context type, which carries deadlines,
|
// Package context has been superseded by the standard library [context] package.
|
||||||
// cancellation signals, and other request-scoped values across API boundaries
|
|
||||||
// and between processes.
|
|
||||||
// As of Go 1.7 this package is available in the standard library under the
|
|
||||||
// name [context], and migrating to it can be done automatically with [go fix].
|
|
||||||
//
|
//
|
||||||
// Incoming requests to a server should create a [Context], and outgoing
|
// Deprecated: Use the standard library context package instead.
|
||||||
// calls to servers should accept a Context. The chain of function
|
|
||||||
// calls between them must propagate the Context, optionally replacing
|
|
||||||
// it with a derived Context created using [WithCancel], [WithDeadline],
|
|
||||||
// [WithTimeout], or [WithValue].
|
|
||||||
//
|
|
||||||
// Programs that use Contexts should follow these rules to keep interfaces
|
|
||||||
// consistent across packages and enable static analysis tools to check context
|
|
||||||
// propagation:
|
|
||||||
//
|
|
||||||
// Do not store Contexts inside a struct type; instead, pass a Context
|
|
||||||
// explicitly to each function that needs it. This is discussed further in
|
|
||||||
// https://go.dev/blog/context-and-structs. The Context should be the first
|
|
||||||
// parameter, typically named ctx:
|
|
||||||
//
|
|
||||||
// func DoSomething(ctx context.Context, arg Arg) error {
|
|
||||||
// // ... use ctx ...
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Do not pass a nil [Context], even if a function permits it. Pass [context.TODO]
|
|
||||||
// if you are unsure about which Context to use.
|
|
||||||
//
|
|
||||||
// Use context Values only for request-scoped data that transits processes and
|
|
||||||
// APIs, not for passing optional parameters to functions.
|
|
||||||
//
|
|
||||||
// The same Context may be passed to functions running in different goroutines;
|
|
||||||
// Contexts are safe for simultaneous use by multiple goroutines.
|
|
||||||
//
|
|
||||||
// See https://go.dev/blog/context for example code for a server that uses
|
|
||||||
// Contexts.
|
|
||||||
//
|
|
||||||
// [go fix]: https://go.dev/cmd/go#hdr-Update_packages_to_use_new_APIs
|
|
||||||
package context
|
package context
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -51,36 +16,37 @@ import (
|
|||||||
// API boundaries.
|
// API boundaries.
|
||||||
//
|
//
|
||||||
// Context's methods may be called by multiple goroutines simultaneously.
|
// Context's methods may be called by multiple goroutines simultaneously.
|
||||||
|
//
|
||||||
|
//go:fix inline
|
||||||
type Context = context.Context
|
type Context = context.Context
|
||||||
|
|
||||||
// Canceled is the error returned by [Context.Err] when the context is canceled
|
// Canceled is the error returned by [Context.Err] when the context is canceled
|
||||||
// for some reason other than its deadline passing.
|
// for some reason other than its deadline passing.
|
||||||
|
//
|
||||||
|
//go:fix inline
|
||||||
var Canceled = context.Canceled
|
var Canceled = context.Canceled
|
||||||
|
|
||||||
// DeadlineExceeded is the error returned by [Context.Err] when the context is canceled
|
// DeadlineExceeded is the error returned by [Context.Err] when the context is canceled
|
||||||
// due to its deadline passing.
|
// due to its deadline passing.
|
||||||
|
//
|
||||||
|
//go:fix inline
|
||||||
var DeadlineExceeded = context.DeadlineExceeded
|
var DeadlineExceeded = context.DeadlineExceeded
|
||||||
|
|
||||||
// Background returns a non-nil, empty Context. It is never canceled, has no
|
// Background returns a non-nil, empty Context. It is never canceled, has no
|
||||||
// values, and has no deadline. It is typically used by the main function,
|
// values, and has no deadline. It is typically used by the main function,
|
||||||
// initialization, and tests, and as the top-level Context for incoming
|
// initialization, and tests, and as the top-level Context for incoming
|
||||||
// requests.
|
// requests.
|
||||||
func Background() Context {
|
//
|
||||||
return background
|
//go:fix inline
|
||||||
}
|
func Background() Context { return context.Background() }
|
||||||
|
|
||||||
// TODO returns a non-nil, empty Context. Code should use context.TODO when
|
// TODO returns a non-nil, empty Context. Code should use context.TODO when
|
||||||
// it's unclear which Context to use or it is not yet available (because the
|
// it's unclear which Context to use or it is not yet available (because the
|
||||||
// surrounding function has not yet been extended to accept a Context
|
// surrounding function has not yet been extended to accept a Context
|
||||||
// parameter).
|
// parameter).
|
||||||
func TODO() Context {
|
//
|
||||||
return todo
|
//go:fix inline
|
||||||
}
|
func TODO() Context { return context.TODO() }
|
||||||
|
|
||||||
var (
|
|
||||||
background = context.Background()
|
|
||||||
todo = context.TODO()
|
|
||||||
)
|
|
||||||
|
|
||||||
// A CancelFunc tells an operation to abandon its work.
|
// A CancelFunc tells an operation to abandon its work.
|
||||||
// A CancelFunc does not wait for the work to stop.
|
// A CancelFunc does not wait for the work to stop.
|
||||||
@@ -95,6 +61,8 @@ type CancelFunc = context.CancelFunc
|
|||||||
//
|
//
|
||||||
// Canceling this context releases resources associated with it, so code should
|
// Canceling this context releases resources associated with it, so code should
|
||||||
// call cancel as soon as the operations running in this [Context] complete.
|
// call cancel as soon as the operations running in this [Context] complete.
|
||||||
|
//
|
||||||
|
//go:fix inline
|
||||||
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
|
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
|
||||||
return context.WithCancel(parent)
|
return context.WithCancel(parent)
|
||||||
}
|
}
|
||||||
@@ -108,6 +76,8 @@ func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
|
|||||||
//
|
//
|
||||||
// Canceling this context releases resources associated with it, so code should
|
// Canceling this context releases resources associated with it, so code should
|
||||||
// call cancel as soon as the operations running in this [Context] complete.
|
// call cancel as soon as the operations running in this [Context] complete.
|
||||||
|
//
|
||||||
|
//go:fix inline
|
||||||
func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) {
|
func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) {
|
||||||
return context.WithDeadline(parent, d)
|
return context.WithDeadline(parent, d)
|
||||||
}
|
}
|
||||||
@@ -122,6 +92,8 @@ func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) {
|
|||||||
// defer cancel() // releases resources if slowOperation completes before timeout elapses
|
// defer cancel() // releases resources if slowOperation completes before timeout elapses
|
||||||
// return slowOperation(ctx)
|
// return slowOperation(ctx)
|
||||||
// }
|
// }
|
||||||
|
//
|
||||||
|
//go:fix inline
|
||||||
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
|
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
|
||||||
return context.WithTimeout(parent, timeout)
|
return context.WithTimeout(parent, timeout)
|
||||||
}
|
}
|
||||||
@@ -139,6 +111,8 @@ func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
|
|||||||
// interface{}, context keys often have concrete type
|
// interface{}, context keys often have concrete type
|
||||||
// struct{}. Alternatively, exported context key variables' static
|
// struct{}. Alternatively, exported context key variables' static
|
||||||
// type should be a pointer or interface.
|
// type should be a pointer or interface.
|
||||||
|
//
|
||||||
|
//go:fix inline
|
||||||
func WithValue(parent Context, key, val interface{}) Context {
|
func WithValue(parent Context, key, val interface{}) Context {
|
||||||
return context.WithValue(parent, key, val)
|
return context.WithValue(parent, key, val)
|
||||||
}
|
}
|
||||||
|
|||||||
168
vendor/golang.org/x/net/internal/socks/client.go
generated
vendored
Normal file
168
vendor/golang.org/x/net/internal/socks/client.go
generated
vendored
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
// Copyright 2018 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.
|
||||||
|
|
||||||
|
package socks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
noDeadline = time.Time{}
|
||||||
|
aLongTimeAgo = time.Unix(1, 0)
|
||||||
|
)
|
||||||
|
|
||||||
|
func (d *Dialer) connect(ctx context.Context, c net.Conn, address string) (_ net.Addr, ctxErr error) {
|
||||||
|
host, port, err := splitHostPort(address)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if deadline, ok := ctx.Deadline(); ok && !deadline.IsZero() {
|
||||||
|
c.SetDeadline(deadline)
|
||||||
|
defer c.SetDeadline(noDeadline)
|
||||||
|
}
|
||||||
|
if ctx != context.Background() {
|
||||||
|
errCh := make(chan error, 1)
|
||||||
|
done := make(chan struct{})
|
||||||
|
defer func() {
|
||||||
|
close(done)
|
||||||
|
if ctxErr == nil {
|
||||||
|
ctxErr = <-errCh
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
c.SetDeadline(aLongTimeAgo)
|
||||||
|
errCh <- ctx.Err()
|
||||||
|
case <-done:
|
||||||
|
errCh <- nil
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
b := make([]byte, 0, 6+len(host)) // the size here is just an estimate
|
||||||
|
b = append(b, Version5)
|
||||||
|
if len(d.AuthMethods) == 0 || d.Authenticate == nil {
|
||||||
|
b = append(b, 1, byte(AuthMethodNotRequired))
|
||||||
|
} else {
|
||||||
|
ams := d.AuthMethods
|
||||||
|
if len(ams) > 255 {
|
||||||
|
return nil, errors.New("too many authentication methods")
|
||||||
|
}
|
||||||
|
b = append(b, byte(len(ams)))
|
||||||
|
for _, am := range ams {
|
||||||
|
b = append(b, byte(am))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if _, ctxErr = c.Write(b); ctxErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ctxErr = io.ReadFull(c, b[:2]); ctxErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if b[0] != Version5 {
|
||||||
|
return nil, errors.New("unexpected protocol version " + strconv.Itoa(int(b[0])))
|
||||||
|
}
|
||||||
|
am := AuthMethod(b[1])
|
||||||
|
if am == AuthMethodNoAcceptableMethods {
|
||||||
|
return nil, errors.New("no acceptable authentication methods")
|
||||||
|
}
|
||||||
|
if d.Authenticate != nil {
|
||||||
|
if ctxErr = d.Authenticate(ctx, c, am); ctxErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b = b[:0]
|
||||||
|
b = append(b, Version5, byte(d.cmd), 0)
|
||||||
|
if ip := net.ParseIP(host); ip != nil {
|
||||||
|
if ip4 := ip.To4(); ip4 != nil {
|
||||||
|
b = append(b, AddrTypeIPv4)
|
||||||
|
b = append(b, ip4...)
|
||||||
|
} else if ip6 := ip.To16(); ip6 != nil {
|
||||||
|
b = append(b, AddrTypeIPv6)
|
||||||
|
b = append(b, ip6...)
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("unknown address type")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if len(host) > 255 {
|
||||||
|
return nil, errors.New("FQDN too long")
|
||||||
|
}
|
||||||
|
b = append(b, AddrTypeFQDN)
|
||||||
|
b = append(b, byte(len(host)))
|
||||||
|
b = append(b, host...)
|
||||||
|
}
|
||||||
|
b = append(b, byte(port>>8), byte(port))
|
||||||
|
if _, ctxErr = c.Write(b); ctxErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ctxErr = io.ReadFull(c, b[:4]); ctxErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if b[0] != Version5 {
|
||||||
|
return nil, errors.New("unexpected protocol version " + strconv.Itoa(int(b[0])))
|
||||||
|
}
|
||||||
|
if cmdErr := Reply(b[1]); cmdErr != StatusSucceeded {
|
||||||
|
return nil, errors.New("unknown error " + cmdErr.String())
|
||||||
|
}
|
||||||
|
if b[2] != 0 {
|
||||||
|
return nil, errors.New("non-zero reserved field")
|
||||||
|
}
|
||||||
|
l := 2
|
||||||
|
var a Addr
|
||||||
|
switch b[3] {
|
||||||
|
case AddrTypeIPv4:
|
||||||
|
l += net.IPv4len
|
||||||
|
a.IP = make(net.IP, net.IPv4len)
|
||||||
|
case AddrTypeIPv6:
|
||||||
|
l += net.IPv6len
|
||||||
|
a.IP = make(net.IP, net.IPv6len)
|
||||||
|
case AddrTypeFQDN:
|
||||||
|
if _, err := io.ReadFull(c, b[:1]); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
l += int(b[0])
|
||||||
|
default:
|
||||||
|
return nil, errors.New("unknown address type " + strconv.Itoa(int(b[3])))
|
||||||
|
}
|
||||||
|
if cap(b) < l {
|
||||||
|
b = make([]byte, l)
|
||||||
|
} else {
|
||||||
|
b = b[:l]
|
||||||
|
}
|
||||||
|
if _, ctxErr = io.ReadFull(c, b); ctxErr != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if a.IP != nil {
|
||||||
|
copy(a.IP, b)
|
||||||
|
} else {
|
||||||
|
a.Name = string(b[:len(b)-2])
|
||||||
|
}
|
||||||
|
a.Port = int(b[len(b)-2])<<8 | int(b[len(b)-1])
|
||||||
|
return &a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func splitHostPort(address string) (string, int, error) {
|
||||||
|
host, port, err := net.SplitHostPort(address)
|
||||||
|
if err != nil {
|
||||||
|
return "", 0, err
|
||||||
|
}
|
||||||
|
portnum, err := strconv.Atoi(port)
|
||||||
|
if err != nil {
|
||||||
|
return "", 0, err
|
||||||
|
}
|
||||||
|
if 1 > portnum || portnum > 0xffff {
|
||||||
|
return "", 0, errors.New("port number out of range " + port)
|
||||||
|
}
|
||||||
|
return host, portnum, nil
|
||||||
|
}
|
||||||
317
vendor/golang.org/x/net/internal/socks/socks.go
generated
vendored
Normal file
317
vendor/golang.org/x/net/internal/socks/socks.go
generated
vendored
Normal file
@@ -0,0 +1,317 @@
|
|||||||
|
// Copyright 2018 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.
|
||||||
|
|
||||||
|
// Package socks provides a SOCKS version 5 client implementation.
|
||||||
|
//
|
||||||
|
// SOCKS protocol version 5 is defined in RFC 1928.
|
||||||
|
// Username/Password authentication for SOCKS version 5 is defined in
|
||||||
|
// RFC 1929.
|
||||||
|
package socks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Command represents a SOCKS command.
|
||||||
|
type Command int
|
||||||
|
|
||||||
|
func (cmd Command) String() string {
|
||||||
|
switch cmd {
|
||||||
|
case CmdConnect:
|
||||||
|
return "socks connect"
|
||||||
|
case cmdBind:
|
||||||
|
return "socks bind"
|
||||||
|
default:
|
||||||
|
return "socks " + strconv.Itoa(int(cmd))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// An AuthMethod represents a SOCKS authentication method.
|
||||||
|
type AuthMethod int
|
||||||
|
|
||||||
|
// A Reply represents a SOCKS command reply code.
|
||||||
|
type Reply int
|
||||||
|
|
||||||
|
func (code Reply) String() string {
|
||||||
|
switch code {
|
||||||
|
case StatusSucceeded:
|
||||||
|
return "succeeded"
|
||||||
|
case 0x01:
|
||||||
|
return "general SOCKS server failure"
|
||||||
|
case 0x02:
|
||||||
|
return "connection not allowed by ruleset"
|
||||||
|
case 0x03:
|
||||||
|
return "network unreachable"
|
||||||
|
case 0x04:
|
||||||
|
return "host unreachable"
|
||||||
|
case 0x05:
|
||||||
|
return "connection refused"
|
||||||
|
case 0x06:
|
||||||
|
return "TTL expired"
|
||||||
|
case 0x07:
|
||||||
|
return "command not supported"
|
||||||
|
case 0x08:
|
||||||
|
return "address type not supported"
|
||||||
|
default:
|
||||||
|
return "unknown code: " + strconv.Itoa(int(code))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wire protocol constants.
|
||||||
|
const (
|
||||||
|
Version5 = 0x05
|
||||||
|
|
||||||
|
AddrTypeIPv4 = 0x01
|
||||||
|
AddrTypeFQDN = 0x03
|
||||||
|
AddrTypeIPv6 = 0x04
|
||||||
|
|
||||||
|
CmdConnect Command = 0x01 // establishes an active-open forward proxy connection
|
||||||
|
cmdBind Command = 0x02 // establishes a passive-open forward proxy connection
|
||||||
|
|
||||||
|
AuthMethodNotRequired AuthMethod = 0x00 // no authentication required
|
||||||
|
AuthMethodUsernamePassword AuthMethod = 0x02 // use username/password
|
||||||
|
AuthMethodNoAcceptableMethods AuthMethod = 0xff // no acceptable authentication methods
|
||||||
|
|
||||||
|
StatusSucceeded Reply = 0x00
|
||||||
|
)
|
||||||
|
|
||||||
|
// An Addr represents a SOCKS-specific address.
|
||||||
|
// Either Name or IP is used exclusively.
|
||||||
|
type Addr struct {
|
||||||
|
Name string // fully-qualified domain name
|
||||||
|
IP net.IP
|
||||||
|
Port int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Addr) Network() string { return "socks" }
|
||||||
|
|
||||||
|
func (a *Addr) String() string {
|
||||||
|
if a == nil {
|
||||||
|
return "<nil>"
|
||||||
|
}
|
||||||
|
port := strconv.Itoa(a.Port)
|
||||||
|
if a.IP == nil {
|
||||||
|
return net.JoinHostPort(a.Name, port)
|
||||||
|
}
|
||||||
|
return net.JoinHostPort(a.IP.String(), port)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Conn represents a forward proxy connection.
|
||||||
|
type Conn struct {
|
||||||
|
net.Conn
|
||||||
|
|
||||||
|
boundAddr net.Addr
|
||||||
|
}
|
||||||
|
|
||||||
|
// BoundAddr returns the address assigned by the proxy server for
|
||||||
|
// connecting to the command target address from the proxy server.
|
||||||
|
func (c *Conn) BoundAddr() net.Addr {
|
||||||
|
if c == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return c.boundAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Dialer holds SOCKS-specific options.
|
||||||
|
type Dialer struct {
|
||||||
|
cmd Command // either CmdConnect or cmdBind
|
||||||
|
proxyNetwork string // network between a proxy server and a client
|
||||||
|
proxyAddress string // proxy server address
|
||||||
|
|
||||||
|
// ProxyDial specifies the optional dial function for
|
||||||
|
// establishing the transport connection.
|
||||||
|
ProxyDial func(context.Context, string, string) (net.Conn, error)
|
||||||
|
|
||||||
|
// AuthMethods specifies the list of request authentication
|
||||||
|
// methods.
|
||||||
|
// If empty, SOCKS client requests only AuthMethodNotRequired.
|
||||||
|
AuthMethods []AuthMethod
|
||||||
|
|
||||||
|
// Authenticate specifies the optional authentication
|
||||||
|
// function. It must be non-nil when AuthMethods is not empty.
|
||||||
|
// It must return an error when the authentication is failed.
|
||||||
|
Authenticate func(context.Context, io.ReadWriter, AuthMethod) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// DialContext connects to the provided address on the provided
|
||||||
|
// network.
|
||||||
|
//
|
||||||
|
// The returned error value may be a net.OpError. When the Op field of
|
||||||
|
// net.OpError contains "socks", the Source field contains a proxy
|
||||||
|
// server address and the Addr field contains a command target
|
||||||
|
// address.
|
||||||
|
//
|
||||||
|
// See func Dial of the net package of standard library for a
|
||||||
|
// description of the network and address parameters.
|
||||||
|
func (d *Dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||||
|
if err := d.validateTarget(network, address); err != nil {
|
||||||
|
proxy, dst, _ := d.pathAddrs(address)
|
||||||
|
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
|
||||||
|
}
|
||||||
|
if ctx == nil {
|
||||||
|
proxy, dst, _ := d.pathAddrs(address)
|
||||||
|
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: errors.New("nil context")}
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
var c net.Conn
|
||||||
|
if d.ProxyDial != nil {
|
||||||
|
c, err = d.ProxyDial(ctx, d.proxyNetwork, d.proxyAddress)
|
||||||
|
} else {
|
||||||
|
var dd net.Dialer
|
||||||
|
c, err = dd.DialContext(ctx, d.proxyNetwork, d.proxyAddress)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
proxy, dst, _ := d.pathAddrs(address)
|
||||||
|
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
|
||||||
|
}
|
||||||
|
a, err := d.connect(ctx, c, address)
|
||||||
|
if err != nil {
|
||||||
|
c.Close()
|
||||||
|
proxy, dst, _ := d.pathAddrs(address)
|
||||||
|
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
|
||||||
|
}
|
||||||
|
return &Conn{Conn: c, boundAddr: a}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DialWithConn initiates a connection from SOCKS server to the target
|
||||||
|
// network and address using the connection c that is already
|
||||||
|
// connected to the SOCKS server.
|
||||||
|
//
|
||||||
|
// It returns the connection's local address assigned by the SOCKS
|
||||||
|
// server.
|
||||||
|
func (d *Dialer) DialWithConn(ctx context.Context, c net.Conn, network, address string) (net.Addr, error) {
|
||||||
|
if err := d.validateTarget(network, address); err != nil {
|
||||||
|
proxy, dst, _ := d.pathAddrs(address)
|
||||||
|
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
|
||||||
|
}
|
||||||
|
if ctx == nil {
|
||||||
|
proxy, dst, _ := d.pathAddrs(address)
|
||||||
|
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: errors.New("nil context")}
|
||||||
|
}
|
||||||
|
a, err := d.connect(ctx, c, address)
|
||||||
|
if err != nil {
|
||||||
|
proxy, dst, _ := d.pathAddrs(address)
|
||||||
|
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
|
||||||
|
}
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dial connects to the provided address on the provided network.
|
||||||
|
//
|
||||||
|
// Unlike DialContext, it returns a raw transport connection instead
|
||||||
|
// of a forward proxy connection.
|
||||||
|
//
|
||||||
|
// Deprecated: Use DialContext or DialWithConn instead.
|
||||||
|
func (d *Dialer) Dial(network, address string) (net.Conn, error) {
|
||||||
|
if err := d.validateTarget(network, address); err != nil {
|
||||||
|
proxy, dst, _ := d.pathAddrs(address)
|
||||||
|
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
var c net.Conn
|
||||||
|
if d.ProxyDial != nil {
|
||||||
|
c, err = d.ProxyDial(context.Background(), d.proxyNetwork, d.proxyAddress)
|
||||||
|
} else {
|
||||||
|
c, err = net.Dial(d.proxyNetwork, d.proxyAddress)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
proxy, dst, _ := d.pathAddrs(address)
|
||||||
|
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
|
||||||
|
}
|
||||||
|
if _, err := d.DialWithConn(context.Background(), c, network, address); err != nil {
|
||||||
|
c.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dialer) validateTarget(network, address string) error {
|
||||||
|
switch network {
|
||||||
|
case "tcp", "tcp6", "tcp4":
|
||||||
|
default:
|
||||||
|
return errors.New("network not implemented")
|
||||||
|
}
|
||||||
|
switch d.cmd {
|
||||||
|
case CmdConnect, cmdBind:
|
||||||
|
default:
|
||||||
|
return errors.New("command not implemented")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dialer) pathAddrs(address string) (proxy, dst net.Addr, err error) {
|
||||||
|
for i, s := range []string{d.proxyAddress, address} {
|
||||||
|
host, port, err := splitHostPort(s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
a := &Addr{Port: port}
|
||||||
|
a.IP = net.ParseIP(host)
|
||||||
|
if a.IP == nil {
|
||||||
|
a.Name = host
|
||||||
|
}
|
||||||
|
if i == 0 {
|
||||||
|
proxy = a
|
||||||
|
} else {
|
||||||
|
dst = a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDialer returns a new Dialer that dials through the provided
|
||||||
|
// proxy server's network and address.
|
||||||
|
func NewDialer(network, address string) *Dialer {
|
||||||
|
return &Dialer{proxyNetwork: network, proxyAddress: address, cmd: CmdConnect}
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
authUsernamePasswordVersion = 0x01
|
||||||
|
authStatusSucceeded = 0x00
|
||||||
|
)
|
||||||
|
|
||||||
|
// UsernamePassword are the credentials for the username/password
|
||||||
|
// authentication method.
|
||||||
|
type UsernamePassword struct {
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Authenticate authenticates a pair of username and password with the
|
||||||
|
// proxy server.
|
||||||
|
func (up *UsernamePassword) Authenticate(ctx context.Context, rw io.ReadWriter, auth AuthMethod) error {
|
||||||
|
switch auth {
|
||||||
|
case AuthMethodNotRequired:
|
||||||
|
return nil
|
||||||
|
case AuthMethodUsernamePassword:
|
||||||
|
if len(up.Username) == 0 || len(up.Username) > 255 || len(up.Password) > 255 {
|
||||||
|
return errors.New("invalid username/password")
|
||||||
|
}
|
||||||
|
b := []byte{authUsernamePasswordVersion}
|
||||||
|
b = append(b, byte(len(up.Username)))
|
||||||
|
b = append(b, up.Username...)
|
||||||
|
b = append(b, byte(len(up.Password)))
|
||||||
|
b = append(b, up.Password...)
|
||||||
|
// TODO(mikio): handle IO deadlines and cancellation if
|
||||||
|
// necessary
|
||||||
|
if _, err := rw.Write(b); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := io.ReadFull(rw, b[:2]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if b[0] != authUsernamePasswordVersion {
|
||||||
|
return errors.New("invalid username/password version")
|
||||||
|
}
|
||||||
|
if b[1] != authStatusSucceeded {
|
||||||
|
return errors.New("username/password authentication failed")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return errors.New("unsupported authentication method " + strconv.Itoa(int(auth)))
|
||||||
|
}
|
||||||
54
vendor/golang.org/x/net/proxy/dial.go
generated
vendored
Normal file
54
vendor/golang.org/x/net/proxy/dial.go
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
package proxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A ContextDialer dials using a context.
|
||||||
|
type ContextDialer interface {
|
||||||
|
DialContext(ctx context.Context, network, address string) (net.Conn, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dial works like DialContext on net.Dialer but using a dialer returned by FromEnvironment.
|
||||||
|
//
|
||||||
|
// The passed ctx is only used for returning the Conn, not the lifetime of the Conn.
|
||||||
|
//
|
||||||
|
// Custom dialers (registered via RegisterDialerType) that do not implement ContextDialer
|
||||||
|
// can leak a goroutine for as long as it takes the underlying Dialer implementation to timeout.
|
||||||
|
//
|
||||||
|
// A Conn returned from a successful Dial after the context has been cancelled will be immediately closed.
|
||||||
|
func Dial(ctx context.Context, network, address string) (net.Conn, error) {
|
||||||
|
d := FromEnvironment()
|
||||||
|
if xd, ok := d.(ContextDialer); ok {
|
||||||
|
return xd.DialContext(ctx, network, address)
|
||||||
|
}
|
||||||
|
return dialContext(ctx, d, network, address)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WARNING: this can leak a goroutine for as long as the underlying Dialer implementation takes to timeout
|
||||||
|
// A Conn returned from a successful Dial after the context has been cancelled will be immediately closed.
|
||||||
|
func dialContext(ctx context.Context, d Dialer, network, address string) (net.Conn, error) {
|
||||||
|
var (
|
||||||
|
conn net.Conn
|
||||||
|
done = make(chan struct{}, 1)
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
go func() {
|
||||||
|
conn, err = d.Dial(network, address)
|
||||||
|
close(done)
|
||||||
|
if conn != nil && ctx.Err() != nil {
|
||||||
|
conn.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
err = ctx.Err()
|
||||||
|
case <-done:
|
||||||
|
}
|
||||||
|
return conn, err
|
||||||
|
}
|
||||||
31
vendor/golang.org/x/net/proxy/direct.go
generated
vendored
Normal file
31
vendor/golang.org/x/net/proxy/direct.go
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
// Copyright 2011 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.
|
||||||
|
|
||||||
|
package proxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
type direct struct{}
|
||||||
|
|
||||||
|
// Direct implements Dialer by making network connections directly using net.Dial or net.DialContext.
|
||||||
|
var Direct = direct{}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ Dialer = Direct
|
||||||
|
_ ContextDialer = Direct
|
||||||
|
)
|
||||||
|
|
||||||
|
// Dial directly invokes net.Dial with the supplied parameters.
|
||||||
|
func (direct) Dial(network, addr string) (net.Conn, error) {
|
||||||
|
return net.Dial(network, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DialContext instantiates a net.Dialer and invokes its DialContext receiver with the supplied parameters.
|
||||||
|
func (direct) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||||
|
var d net.Dialer
|
||||||
|
return d.DialContext(ctx, network, addr)
|
||||||
|
}
|
||||||
153
vendor/golang.org/x/net/proxy/per_host.go
generated
vendored
Normal file
153
vendor/golang.org/x/net/proxy/per_host.go
generated
vendored
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
// Copyright 2011 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.
|
||||||
|
|
||||||
|
package proxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
"net/netip"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A PerHost directs connections to a default Dialer unless the host name
|
||||||
|
// requested matches one of a number of exceptions.
|
||||||
|
type PerHost struct {
|
||||||
|
def, bypass Dialer
|
||||||
|
|
||||||
|
bypassNetworks []*net.IPNet
|
||||||
|
bypassIPs []net.IP
|
||||||
|
bypassZones []string
|
||||||
|
bypassHosts []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPerHost returns a PerHost Dialer that directs connections to either
|
||||||
|
// defaultDialer or bypass, depending on whether the connection matches one of
|
||||||
|
// the configured rules.
|
||||||
|
func NewPerHost(defaultDialer, bypass Dialer) *PerHost {
|
||||||
|
return &PerHost{
|
||||||
|
def: defaultDialer,
|
||||||
|
bypass: bypass,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dial connects to the address addr on the given network through either
|
||||||
|
// defaultDialer or bypass.
|
||||||
|
func (p *PerHost) Dial(network, addr string) (c net.Conn, err error) {
|
||||||
|
host, _, err := net.SplitHostPort(addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.dialerForRequest(host).Dial(network, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DialContext connects to the address addr on the given network through either
|
||||||
|
// defaultDialer or bypass.
|
||||||
|
func (p *PerHost) DialContext(ctx context.Context, network, addr string) (c net.Conn, err error) {
|
||||||
|
host, _, err := net.SplitHostPort(addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
d := p.dialerForRequest(host)
|
||||||
|
if x, ok := d.(ContextDialer); ok {
|
||||||
|
return x.DialContext(ctx, network, addr)
|
||||||
|
}
|
||||||
|
return dialContext(ctx, d, network, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PerHost) dialerForRequest(host string) Dialer {
|
||||||
|
if nip, err := netip.ParseAddr(host); err == nil {
|
||||||
|
ip := net.IP(nip.AsSlice())
|
||||||
|
for _, net := range p.bypassNetworks {
|
||||||
|
if net.Contains(ip) {
|
||||||
|
return p.bypass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, bypassIP := range p.bypassIPs {
|
||||||
|
if bypassIP.Equal(ip) {
|
||||||
|
return p.bypass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return p.def
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, zone := range p.bypassZones {
|
||||||
|
if strings.HasSuffix(host, zone) {
|
||||||
|
return p.bypass
|
||||||
|
}
|
||||||
|
if host == zone[1:] {
|
||||||
|
// For a zone ".example.com", we match "example.com"
|
||||||
|
// too.
|
||||||
|
return p.bypass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, bypassHost := range p.bypassHosts {
|
||||||
|
if bypassHost == host {
|
||||||
|
return p.bypass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return p.def
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddFromString parses a string that contains comma-separated values
|
||||||
|
// specifying hosts that should use the bypass proxy. Each value is either an
|
||||||
|
// IP address, a CIDR range, a zone (*.example.com) or a host name
|
||||||
|
// (localhost). A best effort is made to parse the string and errors are
|
||||||
|
// ignored.
|
||||||
|
func (p *PerHost) AddFromString(s string) {
|
||||||
|
hosts := strings.Split(s, ",")
|
||||||
|
for _, host := range hosts {
|
||||||
|
host = strings.TrimSpace(host)
|
||||||
|
if len(host) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.Contains(host, "/") {
|
||||||
|
// We assume that it's a CIDR address like 127.0.0.0/8
|
||||||
|
if _, net, err := net.ParseCIDR(host); err == nil {
|
||||||
|
p.AddNetwork(net)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if nip, err := netip.ParseAddr(host); err == nil {
|
||||||
|
p.AddIP(net.IP(nip.AsSlice()))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(host, "*.") {
|
||||||
|
p.AddZone(host[1:])
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
p.AddHost(host)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddIP specifies an IP address that will use the bypass proxy. Note that
|
||||||
|
// this will only take effect if a literal IP address is dialed. A connection
|
||||||
|
// to a named host will never match an IP.
|
||||||
|
func (p *PerHost) AddIP(ip net.IP) {
|
||||||
|
p.bypassIPs = append(p.bypassIPs, ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddNetwork specifies an IP range that will use the bypass proxy. Note that
|
||||||
|
// this will only take effect if a literal IP address is dialed. A connection
|
||||||
|
// to a named host will never match.
|
||||||
|
func (p *PerHost) AddNetwork(net *net.IPNet) {
|
||||||
|
p.bypassNetworks = append(p.bypassNetworks, net)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddZone specifies a DNS suffix that will use the bypass proxy. A zone of
|
||||||
|
// "example.com" matches "example.com" and all of its subdomains.
|
||||||
|
func (p *PerHost) AddZone(zone string) {
|
||||||
|
zone = strings.TrimSuffix(zone, ".")
|
||||||
|
if !strings.HasPrefix(zone, ".") {
|
||||||
|
zone = "." + zone
|
||||||
|
}
|
||||||
|
p.bypassZones = append(p.bypassZones, zone)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddHost specifies a host name that will use the bypass proxy.
|
||||||
|
func (p *PerHost) AddHost(host string) {
|
||||||
|
host = strings.TrimSuffix(host, ".")
|
||||||
|
p.bypassHosts = append(p.bypassHosts, host)
|
||||||
|
}
|
||||||
149
vendor/golang.org/x/net/proxy/proxy.go
generated
vendored
Normal file
149
vendor/golang.org/x/net/proxy/proxy.go
generated
vendored
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
// Copyright 2011 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.
|
||||||
|
|
||||||
|
// Package proxy provides support for a variety of protocols to proxy network
|
||||||
|
// data.
|
||||||
|
package proxy // import "golang.org/x/net/proxy"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Dialer is a means to establish a connection.
|
||||||
|
// Custom dialers should also implement ContextDialer.
|
||||||
|
type Dialer interface {
|
||||||
|
// Dial connects to the given address via the proxy.
|
||||||
|
Dial(network, addr string) (c net.Conn, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auth contains authentication parameters that specific Dialers may require.
|
||||||
|
type Auth struct {
|
||||||
|
User, Password string
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromEnvironment returns the dialer specified by the proxy-related
|
||||||
|
// variables in the environment and makes underlying connections
|
||||||
|
// directly.
|
||||||
|
func FromEnvironment() Dialer {
|
||||||
|
return FromEnvironmentUsing(Direct)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromEnvironmentUsing returns the dialer specify by the proxy-related
|
||||||
|
// variables in the environment and makes underlying connections
|
||||||
|
// using the provided forwarding Dialer (for instance, a *net.Dialer
|
||||||
|
// with desired configuration).
|
||||||
|
func FromEnvironmentUsing(forward Dialer) Dialer {
|
||||||
|
allProxy := allProxyEnv.Get()
|
||||||
|
if len(allProxy) == 0 {
|
||||||
|
return forward
|
||||||
|
}
|
||||||
|
|
||||||
|
proxyURL, err := url.Parse(allProxy)
|
||||||
|
if err != nil {
|
||||||
|
return forward
|
||||||
|
}
|
||||||
|
proxy, err := FromURL(proxyURL, forward)
|
||||||
|
if err != nil {
|
||||||
|
return forward
|
||||||
|
}
|
||||||
|
|
||||||
|
noProxy := noProxyEnv.Get()
|
||||||
|
if len(noProxy) == 0 {
|
||||||
|
return proxy
|
||||||
|
}
|
||||||
|
|
||||||
|
perHost := NewPerHost(proxy, forward)
|
||||||
|
perHost.AddFromString(noProxy)
|
||||||
|
return perHost
|
||||||
|
}
|
||||||
|
|
||||||
|
// proxySchemes is a map from URL schemes to a function that creates a Dialer
|
||||||
|
// from a URL with such a scheme.
|
||||||
|
var proxySchemes map[string]func(*url.URL, Dialer) (Dialer, error)
|
||||||
|
|
||||||
|
// RegisterDialerType takes a URL scheme and a function to generate Dialers from
|
||||||
|
// a URL with that scheme and a forwarding Dialer. Registered schemes are used
|
||||||
|
// by FromURL.
|
||||||
|
func RegisterDialerType(scheme string, f func(*url.URL, Dialer) (Dialer, error)) {
|
||||||
|
if proxySchemes == nil {
|
||||||
|
proxySchemes = make(map[string]func(*url.URL, Dialer) (Dialer, error))
|
||||||
|
}
|
||||||
|
proxySchemes[scheme] = f
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromURL returns a Dialer given a URL specification and an underlying
|
||||||
|
// Dialer for it to make network requests.
|
||||||
|
func FromURL(u *url.URL, forward Dialer) (Dialer, error) {
|
||||||
|
var auth *Auth
|
||||||
|
if u.User != nil {
|
||||||
|
auth = new(Auth)
|
||||||
|
auth.User = u.User.Username()
|
||||||
|
if p, ok := u.User.Password(); ok {
|
||||||
|
auth.Password = p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch u.Scheme {
|
||||||
|
case "socks5", "socks5h":
|
||||||
|
addr := u.Hostname()
|
||||||
|
port := u.Port()
|
||||||
|
if port == "" {
|
||||||
|
port = "1080"
|
||||||
|
}
|
||||||
|
return SOCKS5("tcp", net.JoinHostPort(addr, port), auth, forward)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the scheme doesn't match any of the built-in schemes, see if it
|
||||||
|
// was registered by another package.
|
||||||
|
if proxySchemes != nil {
|
||||||
|
if f, ok := proxySchemes[u.Scheme]; ok {
|
||||||
|
return f(u, forward)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errors.New("proxy: unknown scheme: " + u.Scheme)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
allProxyEnv = &envOnce{
|
||||||
|
names: []string{"ALL_PROXY", "all_proxy"},
|
||||||
|
}
|
||||||
|
noProxyEnv = &envOnce{
|
||||||
|
names: []string{"NO_PROXY", "no_proxy"},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// envOnce looks up an environment variable (optionally by multiple
|
||||||
|
// names) once. It mitigates expensive lookups on some platforms
|
||||||
|
// (e.g. Windows).
|
||||||
|
// (Borrowed from net/http/transport.go)
|
||||||
|
type envOnce struct {
|
||||||
|
names []string
|
||||||
|
once sync.Once
|
||||||
|
val string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *envOnce) Get() string {
|
||||||
|
e.once.Do(e.init)
|
||||||
|
return e.val
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *envOnce) init() {
|
||||||
|
for _, n := range e.names {
|
||||||
|
e.val = os.Getenv(n)
|
||||||
|
if e.val != "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset is used by tests
|
||||||
|
func (e *envOnce) reset() {
|
||||||
|
e.once = sync.Once{}
|
||||||
|
e.val = ""
|
||||||
|
}
|
||||||
42
vendor/golang.org/x/net/proxy/socks5.go
generated
vendored
Normal file
42
vendor/golang.org/x/net/proxy/socks5.go
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
// Copyright 2011 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.
|
||||||
|
|
||||||
|
package proxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"golang.org/x/net/internal/socks"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given
|
||||||
|
// address with an optional username and password.
|
||||||
|
// See RFC 1928 and RFC 1929.
|
||||||
|
func SOCKS5(network, address string, auth *Auth, forward Dialer) (Dialer, error) {
|
||||||
|
d := socks.NewDialer(network, address)
|
||||||
|
if forward != nil {
|
||||||
|
if f, ok := forward.(ContextDialer); ok {
|
||||||
|
d.ProxyDial = func(ctx context.Context, network string, address string) (net.Conn, error) {
|
||||||
|
return f.DialContext(ctx, network, address)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
d.ProxyDial = func(ctx context.Context, network string, address string) (net.Conn, error) {
|
||||||
|
return dialContext(ctx, forward, network, address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if auth != nil {
|
||||||
|
up := socks.UsernamePassword{
|
||||||
|
Username: auth.User,
|
||||||
|
Password: auth.Password,
|
||||||
|
}
|
||||||
|
d.AuthMethods = []socks.AuthMethod{
|
||||||
|
socks.AuthMethodNotRequired,
|
||||||
|
socks.AuthMethodUsernamePassword,
|
||||||
|
}
|
||||||
|
d.Authenticate = up.Authenticate
|
||||||
|
}
|
||||||
|
return d, nil
|
||||||
|
}
|
||||||
22
vendor/golang.org/x/sys/PATENTS
generated
vendored
22
vendor/golang.org/x/sys/PATENTS
generated
vendored
@@ -1,22 +0,0 @@
|
|||||||
Additional IP Rights Grant (Patents)
|
|
||||||
|
|
||||||
"This implementation" means the copyrightable works distributed by
|
|
||||||
Google as part of the Go project.
|
|
||||||
|
|
||||||
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
|
||||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
|
||||||
patent license to make, have made, use, offer to sell, sell, import,
|
|
||||||
transfer and otherwise run, modify and propagate the contents of this
|
|
||||||
implementation of Go, where such license applies only to those patent
|
|
||||||
claims, both currently owned or controlled by Google and acquired in
|
|
||||||
the future, licensable by Google that are necessarily infringed by this
|
|
||||||
implementation of Go. This grant does not include claims that would be
|
|
||||||
infringed only as a consequence of further modification of this
|
|
||||||
implementation. If you or your agent or exclusive licensee institute or
|
|
||||||
order or agree to the institution of patent litigation against any
|
|
||||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
|
||||||
that this implementation of Go or any code incorporated within this
|
|
||||||
implementation of Go constitutes direct or contributory patent
|
|
||||||
infringement, or inducement of patent infringement, then any patent
|
|
||||||
rights granted to you under this License for this implementation of Go
|
|
||||||
shall terminate as of the date such litigation is filed.
|
|
||||||
2
vendor/golang.org/x/sys/unix/.gitignore
generated
vendored
2
vendor/golang.org/x/sys/unix/.gitignore
generated
vendored
@@ -1,2 +0,0 @@
|
|||||||
_obj/
|
|
||||||
unix.test
|
|
||||||
184
vendor/golang.org/x/sys/unix/README.md
generated
vendored
184
vendor/golang.org/x/sys/unix/README.md
generated
vendored
@@ -1,184 +0,0 @@
|
|||||||
# Building `sys/unix`
|
|
||||||
|
|
||||||
The sys/unix package provides access to the raw system call interface of the
|
|
||||||
underlying operating system. See: https://godoc.org/golang.org/x/sys/unix
|
|
||||||
|
|
||||||
Porting Go to a new architecture/OS combination or adding syscalls, types, or
|
|
||||||
constants to an existing architecture/OS pair requires some manual effort;
|
|
||||||
however, there are tools that automate much of the process.
|
|
||||||
|
|
||||||
## Build Systems
|
|
||||||
|
|
||||||
There are currently two ways we generate the necessary files. We are currently
|
|
||||||
migrating the build system to use containers so the builds are reproducible.
|
|
||||||
This is being done on an OS-by-OS basis. Please update this documentation as
|
|
||||||
components of the build system change.
|
|
||||||
|
|
||||||
### Old Build System (currently for `GOOS != "linux"`)
|
|
||||||
|
|
||||||
The old build system generates the Go files based on the C header files
|
|
||||||
present on your system. This means that files
|
|
||||||
for a given GOOS/GOARCH pair must be generated on a system with that OS and
|
|
||||||
architecture. This also means that the generated code can differ from system
|
|
||||||
to system, based on differences in the header files.
|
|
||||||
|
|
||||||
To avoid this, if you are using the old build system, only generate the Go
|
|
||||||
files on an installation with unmodified header files. It is also important to
|
|
||||||
keep track of which version of the OS the files were generated from (ex.
|
|
||||||
Darwin 14 vs Darwin 15). This makes it easier to track the progress of changes
|
|
||||||
and have each OS upgrade correspond to a single change.
|
|
||||||
|
|
||||||
To build the files for your current OS and architecture, make sure GOOS and
|
|
||||||
GOARCH are set correctly and run `mkall.sh`. This will generate the files for
|
|
||||||
your specific system. Running `mkall.sh -n` shows the commands that will be run.
|
|
||||||
|
|
||||||
Requirements: bash, go
|
|
||||||
|
|
||||||
### New Build System (currently for `GOOS == "linux"`)
|
|
||||||
|
|
||||||
The new build system uses a Docker container to generate the go files directly
|
|
||||||
from source checkouts of the kernel and various system libraries. This means
|
|
||||||
that on any platform that supports Docker, all the files using the new build
|
|
||||||
system can be generated at once, and generated files will not change based on
|
|
||||||
what the person running the scripts has installed on their computer.
|
|
||||||
|
|
||||||
The OS specific files for the new build system are located in the `${GOOS}`
|
|
||||||
directory, and the build is coordinated by the `${GOOS}/mkall.go` program. When
|
|
||||||
the kernel or system library updates, modify the Dockerfile at
|
|
||||||
`${GOOS}/Dockerfile` to checkout the new release of the source.
|
|
||||||
|
|
||||||
To build all the files under the new build system, you must be on an amd64/Linux
|
|
||||||
system and have your GOOS and GOARCH set accordingly. Running `mkall.sh` will
|
|
||||||
then generate all of the files for all of the GOOS/GOARCH pairs in the new build
|
|
||||||
system. Running `mkall.sh -n` shows the commands that will be run.
|
|
||||||
|
|
||||||
Requirements: bash, go, docker
|
|
||||||
|
|
||||||
## Component files
|
|
||||||
|
|
||||||
This section describes the various files used in the code generation process.
|
|
||||||
It also contains instructions on how to modify these files to add a new
|
|
||||||
architecture/OS or to add additional syscalls, types, or constants. Note that
|
|
||||||
if you are using the new build system, the scripts/programs cannot be called normally.
|
|
||||||
They must be called from within the docker container.
|
|
||||||
|
|
||||||
### asm files
|
|
||||||
|
|
||||||
The hand-written assembly file at `asm_${GOOS}_${GOARCH}.s` implements system
|
|
||||||
call dispatch. There are three entry points:
|
|
||||||
```
|
|
||||||
func Syscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr)
|
|
||||||
func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
|
|
||||||
func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr)
|
|
||||||
```
|
|
||||||
The first and second are the standard ones; they differ only in how many
|
|
||||||
arguments can be passed to the kernel. The third is for low-level use by the
|
|
||||||
ForkExec wrapper. Unlike the first two, it does not call into the scheduler to
|
|
||||||
let it know that a system call is running.
|
|
||||||
|
|
||||||
When porting Go to a new architecture/OS, this file must be implemented for
|
|
||||||
each GOOS/GOARCH pair.
|
|
||||||
|
|
||||||
### mksysnum
|
|
||||||
|
|
||||||
Mksysnum is a Go program located at `${GOOS}/mksysnum.go` (or `mksysnum_${GOOS}.go`
|
|
||||||
for the old system). This program takes in a list of header files containing the
|
|
||||||
syscall number declarations and parses them to produce the corresponding list of
|
|
||||||
Go numeric constants. See `zsysnum_${GOOS}_${GOARCH}.go` for the generated
|
|
||||||
constants.
|
|
||||||
|
|
||||||
Adding new syscall numbers is mostly done by running the build on a sufficiently
|
|
||||||
new installation of the target OS (or updating the source checkouts for the
|
|
||||||
new build system). However, depending on the OS, you may need to update the
|
|
||||||
parsing in mksysnum.
|
|
||||||
|
|
||||||
### mksyscall.go
|
|
||||||
|
|
||||||
The `syscall.go`, `syscall_${GOOS}.go`, `syscall_${GOOS}_${GOARCH}.go` are
|
|
||||||
hand-written Go files which implement system calls (for unix, the specific OS,
|
|
||||||
or the specific OS/Architecture pair respectively) that need special handling
|
|
||||||
and list `//sys` comments giving prototypes for ones that can be generated.
|
|
||||||
|
|
||||||
The mksyscall.go program takes the `//sys` and `//sysnb` comments and converts
|
|
||||||
them into syscalls. This requires the name of the prototype in the comment to
|
|
||||||
match a syscall number in the `zsysnum_${GOOS}_${GOARCH}.go` file. The function
|
|
||||||
prototype can be exported (capitalized) or not.
|
|
||||||
|
|
||||||
Adding a new syscall often just requires adding a new `//sys` function prototype
|
|
||||||
with the desired arguments and a capitalized name so it is exported. However, if
|
|
||||||
you want the interface to the syscall to be different, often one will make an
|
|
||||||
unexported `//sys` prototype, and then write a custom wrapper in
|
|
||||||
`syscall_${GOOS}.go`.
|
|
||||||
|
|
||||||
### types files
|
|
||||||
|
|
||||||
For each OS, there is a hand-written Go file at `${GOOS}/types.go` (or
|
|
||||||
`types_${GOOS}.go` on the old system). This file includes standard C headers and
|
|
||||||
creates Go type aliases to the corresponding C types. The file is then fed
|
|
||||||
through godef to get the Go compatible definitions. Finally, the generated code
|
|
||||||
is fed though mkpost.go to format the code correctly and remove any hidden or
|
|
||||||
private identifiers. This cleaned-up code is written to
|
|
||||||
`ztypes_${GOOS}_${GOARCH}.go`.
|
|
||||||
|
|
||||||
The hardest part about preparing this file is figuring out which headers to
|
|
||||||
include and which symbols need to be `#define`d to get the actual data
|
|
||||||
structures that pass through to the kernel system calls. Some C libraries
|
|
||||||
preset alternate versions for binary compatibility and translate them on the
|
|
||||||
way in and out of system calls, but there is almost always a `#define` that can
|
|
||||||
get the real ones.
|
|
||||||
See `types_darwin.go` and `linux/types.go` for examples.
|
|
||||||
|
|
||||||
To add a new type, add in the necessary include statement at the top of the
|
|
||||||
file (if it is not already there) and add in a type alias line. Note that if
|
|
||||||
your type is significantly different on different architectures, you may need
|
|
||||||
some `#if/#elif` macros in your include statements.
|
|
||||||
|
|
||||||
### mkerrors.sh
|
|
||||||
|
|
||||||
This script is used to generate the system's various constants. This doesn't
|
|
||||||
just include the error numbers and error strings, but also the signal numbers
|
|
||||||
and a wide variety of miscellaneous constants. The constants come from the list
|
|
||||||
of include files in the `includes_${uname}` variable. A regex then picks out
|
|
||||||
the desired `#define` statements, and generates the corresponding Go constants.
|
|
||||||
The error numbers and strings are generated from `#include <errno.h>`, and the
|
|
||||||
signal numbers and strings are generated from `#include <signal.h>`. All of
|
|
||||||
these constants are written to `zerrors_${GOOS}_${GOARCH}.go` via a C program,
|
|
||||||
`_errors.c`, which prints out all the constants.
|
|
||||||
|
|
||||||
To add a constant, add the header that includes it to the appropriate variable.
|
|
||||||
Then, edit the regex (if necessary) to match the desired constant. Avoid making
|
|
||||||
the regex too broad to avoid matching unintended constants.
|
|
||||||
|
|
||||||
### internal/mkmerge
|
|
||||||
|
|
||||||
This program is used to extract duplicate const, func, and type declarations
|
|
||||||
from the generated architecture-specific files listed below, and merge these
|
|
||||||
into a common file for each OS.
|
|
||||||
|
|
||||||
The merge is performed in the following steps:
|
|
||||||
1. Construct the set of common code that is identical in all architecture-specific files.
|
|
||||||
2. Write this common code to the merged file.
|
|
||||||
3. Remove the common code from all architecture-specific files.
|
|
||||||
|
|
||||||
|
|
||||||
## Generated files
|
|
||||||
|
|
||||||
### `zerrors_${GOOS}_${GOARCH}.go`
|
|
||||||
|
|
||||||
A file containing all of the system's generated error numbers, error strings,
|
|
||||||
signal numbers, and constants. Generated by `mkerrors.sh` (see above).
|
|
||||||
|
|
||||||
### `zsyscall_${GOOS}_${GOARCH}.go`
|
|
||||||
|
|
||||||
A file containing all the generated syscalls for a specific GOOS and GOARCH.
|
|
||||||
Generated by `mksyscall.go` (see above).
|
|
||||||
|
|
||||||
### `zsysnum_${GOOS}_${GOARCH}.go`
|
|
||||||
|
|
||||||
A list of numeric constants for all the syscall number of the specific GOOS
|
|
||||||
and GOARCH. Generated by mksysnum (see above).
|
|
||||||
|
|
||||||
### `ztypes_${GOOS}_${GOARCH}.go`
|
|
||||||
|
|
||||||
A file containing Go types for passing into (or returning from) syscalls.
|
|
||||||
Generated by godefs and the types file (see above).
|
|
||||||
86
vendor/golang.org/x/sys/unix/affinity_linux.go
generated
vendored
86
vendor/golang.org/x/sys/unix/affinity_linux.go
generated
vendored
@@ -1,86 +0,0 @@
|
|||||||
// Copyright 2018 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.
|
|
||||||
|
|
||||||
// CPU affinity functions
|
|
||||||
|
|
||||||
package unix
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math/bits"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
const cpuSetSize = _CPU_SETSIZE / _NCPUBITS
|
|
||||||
|
|
||||||
// CPUSet represents a CPU affinity mask.
|
|
||||||
type CPUSet [cpuSetSize]cpuMask
|
|
||||||
|
|
||||||
func schedAffinity(trap uintptr, pid int, set *CPUSet) error {
|
|
||||||
_, _, e := RawSyscall(trap, uintptr(pid), uintptr(unsafe.Sizeof(*set)), uintptr(unsafe.Pointer(set)))
|
|
||||||
if e != 0 {
|
|
||||||
return errnoErr(e)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SchedGetaffinity gets the CPU affinity mask of the thread specified by pid.
|
|
||||||
// If pid is 0 the calling thread is used.
|
|
||||||
func SchedGetaffinity(pid int, set *CPUSet) error {
|
|
||||||
return schedAffinity(SYS_SCHED_GETAFFINITY, pid, set)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SchedSetaffinity sets the CPU affinity mask of the thread specified by pid.
|
|
||||||
// If pid is 0 the calling thread is used.
|
|
||||||
func SchedSetaffinity(pid int, set *CPUSet) error {
|
|
||||||
return schedAffinity(SYS_SCHED_SETAFFINITY, pid, set)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Zero clears the set s, so that it contains no CPUs.
|
|
||||||
func (s *CPUSet) Zero() {
|
|
||||||
for i := range s {
|
|
||||||
s[i] = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func cpuBitsIndex(cpu int) int {
|
|
||||||
return cpu / _NCPUBITS
|
|
||||||
}
|
|
||||||
|
|
||||||
func cpuBitsMask(cpu int) cpuMask {
|
|
||||||
return cpuMask(1 << (uint(cpu) % _NCPUBITS))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set adds cpu to the set s.
|
|
||||||
func (s *CPUSet) Set(cpu int) {
|
|
||||||
i := cpuBitsIndex(cpu)
|
|
||||||
if i < len(s) {
|
|
||||||
s[i] |= cpuBitsMask(cpu)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear removes cpu from the set s.
|
|
||||||
func (s *CPUSet) Clear(cpu int) {
|
|
||||||
i := cpuBitsIndex(cpu)
|
|
||||||
if i < len(s) {
|
|
||||||
s[i] &^= cpuBitsMask(cpu)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsSet reports whether cpu is in the set s.
|
|
||||||
func (s *CPUSet) IsSet(cpu int) bool {
|
|
||||||
i := cpuBitsIndex(cpu)
|
|
||||||
if i < len(s) {
|
|
||||||
return s[i]&cpuBitsMask(cpu) != 0
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Count returns the number of CPUs in the set s.
|
|
||||||
func (s *CPUSet) Count() int {
|
|
||||||
c := 0
|
|
||||||
for _, b := range s {
|
|
||||||
c += bits.OnesCount64(uint64(b))
|
|
||||||
}
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
13
vendor/golang.org/x/sys/unix/aliases.go
generated
vendored
13
vendor/golang.org/x/sys/unix/aliases.go
generated
vendored
@@ -1,13 +0,0 @@
|
|||||||
// Copyright 2018 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 aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
|
|
||||||
|
|
||||||
package unix
|
|
||||||
|
|
||||||
import "syscall"
|
|
||||||
|
|
||||||
type Signal = syscall.Signal
|
|
||||||
type Errno = syscall.Errno
|
|
||||||
type SysProcAttr = syscall.SysProcAttr
|
|
||||||
17
vendor/golang.org/x/sys/unix/asm_aix_ppc64.s
generated
vendored
17
vendor/golang.org/x/sys/unix/asm_aix_ppc64.s
generated
vendored
@@ -1,17 +0,0 @@
|
|||||||
// Copyright 2018 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 gc
|
|
||||||
|
|
||||||
#include "textflag.h"
|
|
||||||
|
|
||||||
//
|
|
||||||
// System calls for ppc64, AIX are implemented in runtime/syscall_aix.go
|
|
||||||
//
|
|
||||||
|
|
||||||
TEXT ·syscall6(SB),NOSPLIT,$0-88
|
|
||||||
JMP syscall·syscall6(SB)
|
|
||||||
|
|
||||||
TEXT ·rawSyscall6(SB),NOSPLIT,$0-88
|
|
||||||
JMP syscall·rawSyscall6(SB)
|
|
||||||
27
vendor/golang.org/x/sys/unix/asm_bsd_386.s
generated
vendored
27
vendor/golang.org/x/sys/unix/asm_bsd_386.s
generated
vendored
@@ -1,27 +0,0 @@
|
|||||||
// Copyright 2021 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 (freebsd || netbsd || openbsd) && gc
|
|
||||||
|
|
||||||
#include "textflag.h"
|
|
||||||
|
|
||||||
// System call support for 386 BSD
|
|
||||||
|
|
||||||
// Just jump to package syscall's implementation for all these functions.
|
|
||||||
// The runtime may know about them.
|
|
||||||
|
|
||||||
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
|
||||||
JMP syscall·Syscall(SB)
|
|
||||||
|
|
||||||
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
|
||||||
JMP syscall·Syscall6(SB)
|
|
||||||
|
|
||||||
TEXT ·Syscall9(SB),NOSPLIT,$0-52
|
|
||||||
JMP syscall·Syscall9(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
|
||||||
JMP syscall·RawSyscall(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
|
||||||
JMP syscall·RawSyscall6(SB)
|
|
||||||
27
vendor/golang.org/x/sys/unix/asm_bsd_amd64.s
generated
vendored
27
vendor/golang.org/x/sys/unix/asm_bsd_amd64.s
generated
vendored
@@ -1,27 +0,0 @@
|
|||||||
// Copyright 2021 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 (darwin || dragonfly || freebsd || netbsd || openbsd) && gc
|
|
||||||
|
|
||||||
#include "textflag.h"
|
|
||||||
|
|
||||||
// System call support for AMD64 BSD
|
|
||||||
|
|
||||||
// Just jump to package syscall's implementation for all these functions.
|
|
||||||
// The runtime may know about them.
|
|
||||||
|
|
||||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
|
||||||
JMP syscall·Syscall(SB)
|
|
||||||
|
|
||||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
|
||||||
JMP syscall·Syscall6(SB)
|
|
||||||
|
|
||||||
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
|
||||||
JMP syscall·Syscall9(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
|
||||||
JMP syscall·RawSyscall(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
|
||||||
JMP syscall·RawSyscall6(SB)
|
|
||||||
27
vendor/golang.org/x/sys/unix/asm_bsd_arm.s
generated
vendored
27
vendor/golang.org/x/sys/unix/asm_bsd_arm.s
generated
vendored
@@ -1,27 +0,0 @@
|
|||||||
// Copyright 2021 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 (freebsd || netbsd || openbsd) && gc
|
|
||||||
|
|
||||||
#include "textflag.h"
|
|
||||||
|
|
||||||
// System call support for ARM BSD
|
|
||||||
|
|
||||||
// Just jump to package syscall's implementation for all these functions.
|
|
||||||
// The runtime may know about them.
|
|
||||||
|
|
||||||
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
|
||||||
B syscall·Syscall(SB)
|
|
||||||
|
|
||||||
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
|
||||||
B syscall·Syscall6(SB)
|
|
||||||
|
|
||||||
TEXT ·Syscall9(SB),NOSPLIT,$0-52
|
|
||||||
B syscall·Syscall9(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
|
||||||
B syscall·RawSyscall(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
|
||||||
B syscall·RawSyscall6(SB)
|
|
||||||
27
vendor/golang.org/x/sys/unix/asm_bsd_arm64.s
generated
vendored
27
vendor/golang.org/x/sys/unix/asm_bsd_arm64.s
generated
vendored
@@ -1,27 +0,0 @@
|
|||||||
// Copyright 2021 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 (darwin || freebsd || netbsd || openbsd) && gc
|
|
||||||
|
|
||||||
#include "textflag.h"
|
|
||||||
|
|
||||||
// System call support for ARM64 BSD
|
|
||||||
|
|
||||||
// Just jump to package syscall's implementation for all these functions.
|
|
||||||
// The runtime may know about them.
|
|
||||||
|
|
||||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
|
||||||
JMP syscall·Syscall(SB)
|
|
||||||
|
|
||||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
|
||||||
JMP syscall·Syscall6(SB)
|
|
||||||
|
|
||||||
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
|
||||||
JMP syscall·Syscall9(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
|
||||||
JMP syscall·RawSyscall(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
|
||||||
JMP syscall·RawSyscall6(SB)
|
|
||||||
29
vendor/golang.org/x/sys/unix/asm_bsd_ppc64.s
generated
vendored
29
vendor/golang.org/x/sys/unix/asm_bsd_ppc64.s
generated
vendored
@@ -1,29 +0,0 @@
|
|||||||
// Copyright 2022 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 (darwin || freebsd || netbsd || openbsd) && gc
|
|
||||||
|
|
||||||
#include "textflag.h"
|
|
||||||
|
|
||||||
//
|
|
||||||
// System call support for ppc64, BSD
|
|
||||||
//
|
|
||||||
|
|
||||||
// Just jump to package syscall's implementation for all these functions.
|
|
||||||
// The runtime may know about them.
|
|
||||||
|
|
||||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
|
||||||
JMP syscall·Syscall(SB)
|
|
||||||
|
|
||||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
|
||||||
JMP syscall·Syscall6(SB)
|
|
||||||
|
|
||||||
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
|
||||||
JMP syscall·Syscall9(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
|
||||||
JMP syscall·RawSyscall(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
|
||||||
JMP syscall·RawSyscall6(SB)
|
|
||||||
27
vendor/golang.org/x/sys/unix/asm_bsd_riscv64.s
generated
vendored
27
vendor/golang.org/x/sys/unix/asm_bsd_riscv64.s
generated
vendored
@@ -1,27 +0,0 @@
|
|||||||
// Copyright 2021 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 (darwin || freebsd || netbsd || openbsd) && gc
|
|
||||||
|
|
||||||
#include "textflag.h"
|
|
||||||
|
|
||||||
// System call support for RISCV64 BSD
|
|
||||||
|
|
||||||
// Just jump to package syscall's implementation for all these functions.
|
|
||||||
// The runtime may know about them.
|
|
||||||
|
|
||||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
|
||||||
JMP syscall·Syscall(SB)
|
|
||||||
|
|
||||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
|
||||||
JMP syscall·Syscall6(SB)
|
|
||||||
|
|
||||||
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
|
||||||
JMP syscall·Syscall9(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
|
||||||
JMP syscall·RawSyscall(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
|
||||||
JMP syscall·RawSyscall6(SB)
|
|
||||||
65
vendor/golang.org/x/sys/unix/asm_linux_386.s
generated
vendored
65
vendor/golang.org/x/sys/unix/asm_linux_386.s
generated
vendored
@@ -1,65 +0,0 @@
|
|||||||
// Copyright 2009 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 gc
|
|
||||||
|
|
||||||
#include "textflag.h"
|
|
||||||
|
|
||||||
//
|
|
||||||
// System calls for 386, Linux
|
|
||||||
//
|
|
||||||
|
|
||||||
// See ../runtime/sys_linux_386.s for the reason why we always use int 0x80
|
|
||||||
// instead of the glibc-specific "CALL 0x10(GS)".
|
|
||||||
#define INVOKE_SYSCALL INT $0x80
|
|
||||||
|
|
||||||
// Just jump to package syscall's implementation for all these functions.
|
|
||||||
// The runtime may know about them.
|
|
||||||
|
|
||||||
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
|
||||||
JMP syscall·Syscall(SB)
|
|
||||||
|
|
||||||
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
|
||||||
JMP syscall·Syscall6(SB)
|
|
||||||
|
|
||||||
TEXT ·SyscallNoError(SB),NOSPLIT,$0-24
|
|
||||||
CALL runtime·entersyscall(SB)
|
|
||||||
MOVL trap+0(FP), AX // syscall entry
|
|
||||||
MOVL a1+4(FP), BX
|
|
||||||
MOVL a2+8(FP), CX
|
|
||||||
MOVL a3+12(FP), DX
|
|
||||||
MOVL $0, SI
|
|
||||||
MOVL $0, DI
|
|
||||||
INVOKE_SYSCALL
|
|
||||||
MOVL AX, r1+16(FP)
|
|
||||||
MOVL DX, r2+20(FP)
|
|
||||||
CALL runtime·exitsyscall(SB)
|
|
||||||
RET
|
|
||||||
|
|
||||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
|
||||||
JMP syscall·RawSyscall(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
|
||||||
JMP syscall·RawSyscall6(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-24
|
|
||||||
MOVL trap+0(FP), AX // syscall entry
|
|
||||||
MOVL a1+4(FP), BX
|
|
||||||
MOVL a2+8(FP), CX
|
|
||||||
MOVL a3+12(FP), DX
|
|
||||||
MOVL $0, SI
|
|
||||||
MOVL $0, DI
|
|
||||||
INVOKE_SYSCALL
|
|
||||||
MOVL AX, r1+16(FP)
|
|
||||||
MOVL DX, r2+20(FP)
|
|
||||||
RET
|
|
||||||
|
|
||||||
TEXT ·socketcall(SB),NOSPLIT,$0-36
|
|
||||||
JMP syscall·socketcall(SB)
|
|
||||||
|
|
||||||
TEXT ·rawsocketcall(SB),NOSPLIT,$0-36
|
|
||||||
JMP syscall·rawsocketcall(SB)
|
|
||||||
|
|
||||||
TEXT ·seek(SB),NOSPLIT,$0-28
|
|
||||||
JMP syscall·seek(SB)
|
|
||||||
57
vendor/golang.org/x/sys/unix/asm_linux_amd64.s
generated
vendored
57
vendor/golang.org/x/sys/unix/asm_linux_amd64.s
generated
vendored
@@ -1,57 +0,0 @@
|
|||||||
// Copyright 2009 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 gc
|
|
||||||
|
|
||||||
#include "textflag.h"
|
|
||||||
|
|
||||||
//
|
|
||||||
// System calls for AMD64, Linux
|
|
||||||
//
|
|
||||||
|
|
||||||
// Just jump to package syscall's implementation for all these functions.
|
|
||||||
// The runtime may know about them.
|
|
||||||
|
|
||||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
|
||||||
JMP syscall·Syscall(SB)
|
|
||||||
|
|
||||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
|
||||||
JMP syscall·Syscall6(SB)
|
|
||||||
|
|
||||||
TEXT ·SyscallNoError(SB),NOSPLIT,$0-48
|
|
||||||
CALL runtime·entersyscall(SB)
|
|
||||||
MOVQ a1+8(FP), DI
|
|
||||||
MOVQ a2+16(FP), SI
|
|
||||||
MOVQ a3+24(FP), DX
|
|
||||||
MOVQ $0, R10
|
|
||||||
MOVQ $0, R8
|
|
||||||
MOVQ $0, R9
|
|
||||||
MOVQ trap+0(FP), AX // syscall entry
|
|
||||||
SYSCALL
|
|
||||||
MOVQ AX, r1+32(FP)
|
|
||||||
MOVQ DX, r2+40(FP)
|
|
||||||
CALL runtime·exitsyscall(SB)
|
|
||||||
RET
|
|
||||||
|
|
||||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
|
||||||
JMP syscall·RawSyscall(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
|
||||||
JMP syscall·RawSyscall6(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48
|
|
||||||
MOVQ a1+8(FP), DI
|
|
||||||
MOVQ a2+16(FP), SI
|
|
||||||
MOVQ a3+24(FP), DX
|
|
||||||
MOVQ $0, R10
|
|
||||||
MOVQ $0, R8
|
|
||||||
MOVQ $0, R9
|
|
||||||
MOVQ trap+0(FP), AX // syscall entry
|
|
||||||
SYSCALL
|
|
||||||
MOVQ AX, r1+32(FP)
|
|
||||||
MOVQ DX, r2+40(FP)
|
|
||||||
RET
|
|
||||||
|
|
||||||
TEXT ·gettimeofday(SB),NOSPLIT,$0-16
|
|
||||||
JMP syscall·gettimeofday(SB)
|
|
||||||
56
vendor/golang.org/x/sys/unix/asm_linux_arm.s
generated
vendored
56
vendor/golang.org/x/sys/unix/asm_linux_arm.s
generated
vendored
@@ -1,56 +0,0 @@
|
|||||||
// Copyright 2009 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 gc
|
|
||||||
|
|
||||||
#include "textflag.h"
|
|
||||||
|
|
||||||
//
|
|
||||||
// System calls for arm, Linux
|
|
||||||
//
|
|
||||||
|
|
||||||
// Just jump to package syscall's implementation for all these functions.
|
|
||||||
// The runtime may know about them.
|
|
||||||
|
|
||||||
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
|
||||||
B syscall·Syscall(SB)
|
|
||||||
|
|
||||||
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
|
||||||
B syscall·Syscall6(SB)
|
|
||||||
|
|
||||||
TEXT ·SyscallNoError(SB),NOSPLIT,$0-24
|
|
||||||
BL runtime·entersyscall(SB)
|
|
||||||
MOVW trap+0(FP), R7
|
|
||||||
MOVW a1+4(FP), R0
|
|
||||||
MOVW a2+8(FP), R1
|
|
||||||
MOVW a3+12(FP), R2
|
|
||||||
MOVW $0, R3
|
|
||||||
MOVW $0, R4
|
|
||||||
MOVW $0, R5
|
|
||||||
SWI $0
|
|
||||||
MOVW R0, r1+16(FP)
|
|
||||||
MOVW $0, R0
|
|
||||||
MOVW R0, r2+20(FP)
|
|
||||||
BL runtime·exitsyscall(SB)
|
|
||||||
RET
|
|
||||||
|
|
||||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
|
||||||
B syscall·RawSyscall(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
|
||||||
B syscall·RawSyscall6(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-24
|
|
||||||
MOVW trap+0(FP), R7 // syscall entry
|
|
||||||
MOVW a1+4(FP), R0
|
|
||||||
MOVW a2+8(FP), R1
|
|
||||||
MOVW a3+12(FP), R2
|
|
||||||
SWI $0
|
|
||||||
MOVW R0, r1+16(FP)
|
|
||||||
MOVW $0, R0
|
|
||||||
MOVW R0, r2+20(FP)
|
|
||||||
RET
|
|
||||||
|
|
||||||
TEXT ·seek(SB),NOSPLIT,$0-28
|
|
||||||
B syscall·seek(SB)
|
|
||||||
50
vendor/golang.org/x/sys/unix/asm_linux_arm64.s
generated
vendored
50
vendor/golang.org/x/sys/unix/asm_linux_arm64.s
generated
vendored
@@ -1,50 +0,0 @@
|
|||||||
// Copyright 2015 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 linux && arm64 && gc
|
|
||||||
|
|
||||||
#include "textflag.h"
|
|
||||||
|
|
||||||
// Just jump to package syscall's implementation for all these functions.
|
|
||||||
// The runtime may know about them.
|
|
||||||
|
|
||||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
|
||||||
B syscall·Syscall(SB)
|
|
||||||
|
|
||||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
|
||||||
B syscall·Syscall6(SB)
|
|
||||||
|
|
||||||
TEXT ·SyscallNoError(SB),NOSPLIT,$0-48
|
|
||||||
BL runtime·entersyscall(SB)
|
|
||||||
MOVD a1+8(FP), R0
|
|
||||||
MOVD a2+16(FP), R1
|
|
||||||
MOVD a3+24(FP), R2
|
|
||||||
MOVD $0, R3
|
|
||||||
MOVD $0, R4
|
|
||||||
MOVD $0, R5
|
|
||||||
MOVD trap+0(FP), R8 // syscall entry
|
|
||||||
SVC
|
|
||||||
MOVD R0, r1+32(FP) // r1
|
|
||||||
MOVD R1, r2+40(FP) // r2
|
|
||||||
BL runtime·exitsyscall(SB)
|
|
||||||
RET
|
|
||||||
|
|
||||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
|
||||||
B syscall·RawSyscall(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
|
||||||
B syscall·RawSyscall6(SB)
|
|
||||||
|
|
||||||
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-48
|
|
||||||
MOVD a1+8(FP), R0
|
|
||||||
MOVD a2+16(FP), R1
|
|
||||||
MOVD a3+24(FP), R2
|
|
||||||
MOVD $0, R3
|
|
||||||
MOVD $0, R4
|
|
||||||
MOVD $0, R5
|
|
||||||
MOVD trap+0(FP), R8 // syscall entry
|
|
||||||
SVC
|
|
||||||
MOVD R0, r1+32(FP)
|
|
||||||
MOVD R1, r2+40(FP)
|
|
||||||
RET
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user