mirror of
https://github.com/phishingclub/phishingclub.git
synced 2026-08-17 16:07:18 +02:00
+8
-8
@@ -19,12 +19,12 @@ require (
|
||||
github.com/oapi-codegen/nullable v1.1.0
|
||||
github.com/pquerna/otp v1.4.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/wneessen/go-mail v0.4.2-0.20240324213705-f60ef348aa29
|
||||
github.com/wneessen/go-mail v0.7.1
|
||||
github.com/yeqown/go-qrcode/v2 v2.2.4
|
||||
go.uber.org/zap v1.27.0
|
||||
golang.org/x/crypto v0.37.0
|
||||
golang.org/x/mod v0.24.0
|
||||
golang.org/x/net v0.38.0
|
||||
golang.org/x/crypto v0.41.0
|
||||
golang.org/x/mod v0.27.0
|
||||
golang.org/x/net v0.43.0
|
||||
golang.org/x/time v0.3.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
gorm.io/driver/sqlite v1.6.0
|
||||
@@ -82,9 +82,9 @@ require (
|
||||
github.com/zeebo/blake3 v0.2.3 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/arch v0.9.0 // indirect
|
||||
golang.org/x/sync v0.13.0 // indirect
|
||||
golang.org/x/sys v0.32.0 // indirect
|
||||
golang.org/x/text v0.24.0 // indirect
|
||||
golang.org/x/tools v0.29.0 // indirect
|
||||
golang.org/x/sync v0.17.0 // indirect
|
||||
golang.org/x/sys v0.35.0 // indirect
|
||||
golang.org/x/text v0.29.0 // indirect
|
||||
golang.org/x/tools v0.36.0 // indirect
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
)
|
||||
|
||||
+16
-16
@@ -143,8 +143,8 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/wneessen/go-mail v0.4.2-0.20240324213705-f60ef348aa29 h1:qIrOwcLsH4ZabL4lM3wXKy+/61x2c8ytC4DBLoQd7Og=
|
||||
github.com/wneessen/go-mail v0.4.2-0.20240324213705-f60ef348aa29/go.mod h1:zxOlafWCP/r6FEhAaRgH4IC1vg2YXxO0Nar9u0IScZ8=
|
||||
github.com/wneessen/go-mail v0.7.1 h1:rvy63sp14N06/kdGqCYwW8Na5gDCXjTQM1E7So4PuKk=
|
||||
github.com/wneessen/go-mail v0.7.1/go.mod h1:+TkW6QP3EVkgTEqHtVmnAE/1MRhmzb8Y9/W3pweuS+k=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
github.com/yeqown/go-qrcode/v2 v2.2.4 h1:cXdYlrhzHzVAnJHiwr/T6lAUmS9MtEStjEZBjArrvnc=
|
||||
@@ -165,28 +165,28 @@ go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
golang.org/x/arch v0.9.0 h1:ub9TgUInamJ8mrZIGlBG6/4TqWeMszd4N8lNorbrr6k=
|
||||
golang.org/x/arch v0.9.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
|
||||
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
|
||||
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E=
|
||||
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
|
||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
||||
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
||||
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
|
||||
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
|
||||
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
|
||||
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
|
||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
|
||||
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
|
||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
|
||||
golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
|
||||
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
|
||||
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
# SPDX-FileCopyrightText: 2022 Winni Neessen <winni@neessen.dev>
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
freebsd_task:
|
||||
name: FreeBSD
|
||||
|
||||
matrix:
|
||||
- name: FreeBSD 13.2
|
||||
freebsd_instance:
|
||||
image_family: freebsd-13-2
|
||||
- name: FreeBSD 14.0
|
||||
freebsd_instance:
|
||||
image_family: freebsd-14-0
|
||||
|
||||
env:
|
||||
TEST_ALLOW_SEND: 0
|
||||
TEST_SKIP_SENDMAIL: 1
|
||||
|
||||
pkginstall_script:
|
||||
- pkg update -f
|
||||
- pkg install -y go
|
||||
|
||||
test_script:
|
||||
- go test -v -race -cover -shuffle=on ./...
|
||||
+5
-3
@@ -1,6 +1,6 @@
|
||||
# SPDX-FileCopyrightText: 2022 Winni Neessen <winni@neessen.dev>
|
||||
# SPDX-FileCopyrightText: The go-mail Authors
|
||||
#
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
@@ -56,4 +56,6 @@ crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
testdata
|
||||
## Coverage data
|
||||
coverage.coverprofile
|
||||
coverage.html
|
||||
+138
-4
@@ -1,11 +1,145 @@
|
||||
## SPDX-FileCopyrightText: 2022 Winni Neessen <winni@neessen.dev>
|
||||
## SPDX-FileCopyrightText: The go-mail Authors
|
||||
##
|
||||
## SPDX-License-Identifier: MIT
|
||||
|
||||
version = '2'
|
||||
|
||||
[run]
|
||||
go = "1.22"
|
||||
go = '1.24'
|
||||
tests = true
|
||||
|
||||
[linters]
|
||||
enable = ["stylecheck", "whitespace", "containedctx", "contextcheck", "decorder",
|
||||
"errname", "errorlint", "gofmt", "gofumpt"]
|
||||
enable = [
|
||||
'containedctx',
|
||||
'contextcheck',
|
||||
'decorder',
|
||||
'errname',
|
||||
'errorlint',
|
||||
'gosec',
|
||||
'staticcheck',
|
||||
'whitespace'
|
||||
]
|
||||
|
||||
[linters.exclusions]
|
||||
generated = 'lax'
|
||||
presets = [
|
||||
'comments',
|
||||
'common-false-positives',
|
||||
'legacy',
|
||||
'std-error-handling'
|
||||
]
|
||||
paths = [
|
||||
'examples',
|
||||
'third_party$',
|
||||
'builtin$',
|
||||
'examples$'
|
||||
]
|
||||
|
||||
## An overflow is impossible here
|
||||
[[linters.exclusions.rules]]
|
||||
linters = [
|
||||
'gosec'
|
||||
]
|
||||
path = 'random.go'
|
||||
text = 'G115:'
|
||||
|
||||
## These are tests which intentionally do not need any TLS settings
|
||||
[[linters.exclusions.rules]]
|
||||
linters = [
|
||||
'gosec'
|
||||
]
|
||||
path = 'client_test.go'
|
||||
text = 'G402:'
|
||||
|
||||
## These are tests which intentionally do not need any TLS settings
|
||||
[[linters.exclusions.rules]]
|
||||
linters = [
|
||||
'gosec'
|
||||
]
|
||||
path = 'smtp/smtp_test.go'
|
||||
text = 'G402:'
|
||||
|
||||
## We do not dictate a TLS minimum version in the smtp package. go-mail
|
||||
## itself does set sane defaults
|
||||
[[linters.exclusions.rules]]
|
||||
linters = [
|
||||
'gosec'
|
||||
]
|
||||
path = 'smtp/smtp.go'
|
||||
text = 'G402:'
|
||||
|
||||
## The chance that we write +2 million tests is very low, I think we can
|
||||
## ignore this for the time being
|
||||
[[linters.exclusions.rules]]
|
||||
linters = [
|
||||
'gosec'
|
||||
]
|
||||
path = 'client_test.go'
|
||||
text = 'G109:'
|
||||
|
||||
## The chance that we write +2 million tests is very low, I think we can
|
||||
## ignore this for the time being
|
||||
[[linters.exclusions.rules]]
|
||||
linters = [
|
||||
'gosec'
|
||||
]
|
||||
path = 'smtp/smtp_test.go'
|
||||
text = 'G109:'
|
||||
|
||||
## We inform the user about the deprecated status of CRAM-MD5 and suggest
|
||||
## to use SCRAM-SHA instead
|
||||
[[linters.exclusions.rules]]
|
||||
linters = [
|
||||
'gosec'
|
||||
]
|
||||
path = 'smtp/auth_cram_md5.go'
|
||||
text = 'G501:'
|
||||
|
||||
## Yes, SHA1 is weak, but in the context of SCRAM it is still considered
|
||||
## secure for specific applications. The user is information about this
|
||||
## in the documentation
|
||||
[[linters.exclusions.rules]]
|
||||
linters = [
|
||||
'gosec'
|
||||
]
|
||||
path = 'smtp/auth_scram.go'
|
||||
text = 'G505:'
|
||||
|
||||
## Test code for SCRAM-SHA1. Can be ignored.
|
||||
[[linters.exclusions.rules]]
|
||||
linters = [
|
||||
'gosec'
|
||||
]
|
||||
path = 'smtp/smtp_test.go'
|
||||
text = 'G505:'
|
||||
|
||||
## These are tests which intentionally do not need any TLS settings
|
||||
[[linters.exclusions.rules]]
|
||||
linters = [
|
||||
'gosec'
|
||||
]
|
||||
path = 'quicksend_test.go'
|
||||
text = 'G402:'
|
||||
|
||||
## These are tests which intentionally test SHA1 and SHA256
|
||||
[[linters.exclusions.rules]]
|
||||
linters = [
|
||||
'gosec'
|
||||
]
|
||||
path = 'internal/pbkdf2/pbkdf2_test.go'
|
||||
text = 'G505:'
|
||||
|
||||
[formatters]
|
||||
enable = [
|
||||
'gofmt',
|
||||
'gofumpt'
|
||||
]
|
||||
|
||||
[formatters.exclusions]
|
||||
generated = 'lax'
|
||||
paths = [
|
||||
'examples',
|
||||
'third_party$',
|
||||
'builtin$',
|
||||
'examples$'
|
||||
]
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2022 Winni Neessen <winni@neessen.dev>
|
||||
SPDX-FileCopyrightText: Winni Neessen <winni@neessen.dev>
|
||||
|
||||
SPDX-License-Identifier: CC0-1.0
|
||||
SPDX-License-Identifier: MIT
|
||||
-->
|
||||
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2022-2023 The go-mail Authors
|
||||
SPDX-FileCopyrightText: The go-mail Authors
|
||||
|
||||
SPDX-License-Identifier: CC0-1.0
|
||||
SPDX-License-Identifier: MIT
|
||||
-->
|
||||
|
||||
# How to contribute
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022-2023 The go-mail Authors
|
||||
Copyright (c) 2022-2025 The go-mail Authors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
+60
-37
@@ -1,7 +1,7 @@
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2022-2023 The go-mail Authors
|
||||
SPDX-FileCopyrightText: The go-mail Authors
|
||||
|
||||
SPDX-License-Identifier: CC0-1.0
|
||||
SPDX-License-Identifier: MIT
|
||||
-->
|
||||
|
||||
# go-mail - Easy to use, yet comprehensive library for sending mails with Go
|
||||
@@ -16,47 +16,58 @@ SPDX-License-Identifier: CC0-1.0
|
||||
[](https://securityscorecards.dev/viewer/?uri=github.com/wneessen/go-mail)
|
||||
<a href="https://ko-fi.com/D1D24V9IX"><img src="https://uploads-ssl.webflow.com/5c14e387dab576fe667689cf/5cbed8a4ae2b88347c06c923_BuyMeACoffee_blue.png" height="20" alt="buy ma a coffee"></a>
|
||||
|
||||
<p align="center"><img src="./assets/gopher2.svg" width="250" alt="go-mail logo"/></p>
|
||||
<p style="text-align: center"><img src="./assets/gopher2.svg" width="250" alt="go-mail logo"/></p>
|
||||
|
||||
The main idea of this library was to provide a simple interface to sending mails for
|
||||
The main idea of this library was to provide a simple interface for sending mails to
|
||||
my [JS-Mailer](https://github.com/wneessen/js-mailer) project. It quickly evolved into a full-fledged mail library.
|
||||
|
||||
go-mail follows idiomatic Go style and best practice. It's only dependency is the Go Standard Library. It combines a lot
|
||||
of functionality from the standard library to give easy and convenient access to mail and SMTP related tasks.
|
||||
go-mail follows idiomatic Go style and best practice. It has a small dependency footprint by mainly relying on the
|
||||
Go Standard Library and the Go extended packages. It combines a lot of functionality from the standard library to
|
||||
give easy and convenient access to mail and SMTP related tasks.
|
||||
|
||||
Parts of this library (especially some parts of [msgwriter.go](msgwriter.go)) have been forked/ported from the
|
||||
[go-mail/mail](https://github.com/go-mail/mail) respectively [go-gomail/gomail](https://github.com/go-gomail/gomail)
|
||||
which both seems to not be maintained anymore.
|
||||
In the early days, parts of this library (especially some parts of [msgwriter.go](msgwriter.go)) had been
|
||||
forked/ported from [go-mail/mail](https://github.com/go-mail/mail) and respectively [go-gomail/gomail](https://github.com/go-gomail/gomail). Today
|
||||
most of the ported code has been refactored.
|
||||
|
||||
The smtp package of go-mail is forked from the original Go stdlib's `net/smtp` and then extended by the go-mail
|
||||
team.
|
||||
The `smtp` package of go-mail has been forked from the original Go stdlib's `net/smtp` package and has then been extended
|
||||
by the go-mail team to fit the packages needs (more SMTP Auth methods, logging, concurrency-safety, etc.).
|
||||
|
||||
## Features
|
||||
|
||||
Some of the features of this library:
|
||||
Here are some highlights of go-mail's featureset:
|
||||
|
||||
* [X] Only Standard Library dependant
|
||||
* [X] Very small dependency footprint (mainly Go Stdlib and Go extended packages)
|
||||
* [X] Modern, idiomatic Go
|
||||
* [X] Sane and secure defaults
|
||||
* [X] Explicit SSL/TLS support
|
||||
* [X] Implicit StartTLS support with different policies
|
||||
* [X] Implicit SSL/TLS support
|
||||
* [X] Explicit STARTTLS support with different policies
|
||||
* [X] Makes use of contexts for a better control flow and timeout/cancelation handling
|
||||
* [X] SMTP Auth support (LOGIN, PLAIN, CRAM-MD, XOAUTH2)
|
||||
* [X] SMTP Auth support
|
||||
* [X] CRAM-MD5
|
||||
* [X] LOGIN
|
||||
* [X] PLAIN
|
||||
* [X] SCRAM-SHA-1/SCRAM-SHA-1-PLUS
|
||||
* [X] SCRAM-SHA-256/SCRAM-SHA-256-PLUS
|
||||
* [X] XOAUTH2
|
||||
* [X] RFC5322 compliant mail address validation
|
||||
* [X] Support for common mail header field generation (Message-ID, Date, Bulk-Precedence, Priority, etc.)
|
||||
* [X] Reusing the same SMTP connection to send multiple mails
|
||||
* [X] Support for attachments and inline embeds (from file system, `io.Reader` or `embed.FS`)
|
||||
* [X] Concurrency-safe reusing the same SMTP connection to send multiple mails
|
||||
* [X] Support for attachments and inline embeds (from file system, `io.Reader`, `embed.FS` or `fs.FS`)
|
||||
* [X] Support for different encodings
|
||||
* [X] Middleware support for 3rd-party libraries to alter mail messages
|
||||
* [X] Support sending mails via a local sendmail command
|
||||
* [X] Support for requestng MDNs (RFC 8098) and DSNs (RFC 1891)
|
||||
* [X] DKIM signature support via [go-mail-middlware](https://github.com/wneessen/go-mail-middleware)
|
||||
* [X] Message object satisfies `io.WriteTo` and `io.Reader` interfaces
|
||||
* [X] Message object satisfies `io.WriterTo` and `io.Reader` interfaces
|
||||
* [X] Support for Go's `html/template` and `text/template` (as message body, alternative part or attachment/emebed)
|
||||
* [X] Output to file support which allows storing mail messages as e. g. `.eml` files to disk to open them in a MUA
|
||||
* [X] Debug logging of SMTP traffic
|
||||
* [X] Custom error types for delivery errors
|
||||
* [X] Custom dial-context functions for more control over the connection (proxing, DNS hooking, etc.)
|
||||
* [X] Output a go-mail message as EML file and parse EML file into a go-mail message
|
||||
* [X] S/MIME message signing support (RSA and ECDSA)
|
||||
* [X] UNIX domain socket support
|
||||
* [X] Pluggable SMTP error registry for advanced handling of non-RFC-conforming servers
|
||||
|
||||
go-mail works like a programatic email client and provides lots of methods and functionalities you would consider
|
||||
standard in a MUA.
|
||||
@@ -67,15 +78,17 @@ documentation website at [go-mail.dev](https://go-mail.dev)
|
||||
|
||||
## Compatibility
|
||||
|
||||
Go is growing fast and providing great features with every new release. While we'd love to adopt the latest Go features
|
||||
into our code, we realize that not everybody using this package can run the latest Go versions. Therefore we try to
|
||||
implement alternative solutions for Go versions that do not support these features. Yet, the work needed to maintain
|
||||
the separate versions is not to be underestimated. For that reason, we might retire that code at some point.
|
||||
We guarantee that go-mail will always support the last four releases of Go. With two Go releases per year, this gives
|
||||
the user a timeframe of two years to update to the next or even the latest version of Go.
|
||||
Go evolves quickly and introduces valuable improvements with each release. To balance adopting new features with
|
||||
ensuring stability for our users, we align our support with the official Go release policy. In practice, go-mail
|
||||
will always support the same set of Go versions that the Go team actively maintains.
|
||||
|
||||
Since Go provides two releases per year, this translates into roughly one year of guaranteed compatibility for
|
||||
any given Go version. We encourage users to stay current with supported Go versions to benefit from security
|
||||
updates, performance improvements, and new language features.
|
||||
|
||||
## Support
|
||||
We have a support and general discussion channel on Discord. Find us at: [#go-mail](https://discord.gg/dbfQyC4s)
|
||||
We have a support and general discussion channel on Discord. Find us at: [#go-mail](https://discord.gg/dbfQyC4s) alternatively find us
|
||||
on the [Gophers Slack](https://gophers.slack.com) in #go-mail
|
||||
|
||||
## Middleware
|
||||
The goal of go-mail is to keep it free from 3rd party dependencies and only focus on things a mail library should
|
||||
@@ -85,21 +98,31 @@ alter a given mail message to their needs without relying on `go-mail` to suppor
|
||||
To get our users started with message middleware, we've created a collection of useful middlewares. It can be
|
||||
found in a seperate repository: [go-mail-middlware](https://github.com/wneessen/go-mail-middleware).
|
||||
|
||||
## Merch
|
||||
Thanks to our wonderful friends at [HelloTux](https://www.hellotux.com) we can offer great go-mail merchandising. All merch articles are embroidery
|
||||
to provide the best and most long-lasting quality possible.
|
||||
|
||||
If you want to support the open source community and represent your favourite Go mail library with some cool drip, check out our merch shop at:
|
||||
[https://www.hellotux.com/go-mail](https://www.hellotux.com/go-mail).
|
||||
|
||||
## Examples
|
||||
|
||||
We provide example code in both our GoDocs as well as on our official Website (see [Documentation](#documentation)). For a quick start into go-mail
|
||||
check out our [Getting started](https://go-mail.dev/getting-started/introduction/) guide.
|
||||
|
||||
## Authors/Contributors
|
||||
go-mail was initially authored and developed by [Winni Neessen](https://github.com/wneessen/).
|
||||
go-mail was initially created and developed by [Winni Neessen](https://github.com/wneessen/), but over time a lot of amazing people
|
||||
contributed ot the project. Big thanks to all of them for improving the go-mail project (be it writing code, testing
|
||||
code, reviewing code, writing documenation or helping to translate the website):
|
||||
|
||||
Big thanks to the following people, for contributing to the go-mail project (either in form of code or by
|
||||
reviewing code, writing documenation or helping to translate the website):
|
||||
* [Christian Vette](https://github.com/cvette)
|
||||
* [Dhia Gharsallaoui](https://github.com/dhia-gharsallaoui)
|
||||
* [inliquid](https://github.com/inliquid)
|
||||
* [iwittkau](https://github.com/iwittkau)
|
||||
* [James Elliott](https://github.com/james-d-elliott)
|
||||
* [Maria Letta](https://github.com/MariaLetta) (designed the go-mail logo)
|
||||
* [Nicola Murino](https://github.com/drakkan)
|
||||
* [sters](https://github.com/sters)
|
||||
<a href="https://github.com/wneessen/go-mail/graphs/contributors">
|
||||
<img alt="image of contributors" src="https://contrib.rocks/image?repo=wneessen/go-mail" />
|
||||
</a>
|
||||
|
||||
A huge thank you also goes to [Maria Letta](https://github.com/MariaLetta) for designing our super cool go-mail logo!
|
||||
|
||||
## Sponsors
|
||||
We sincerely thank our amazing sponsors for their generous support! Your contributions do not go unnoticed and helps
|
||||
keeping up the project!
|
||||
|
||||
* [kolaente](https://github.com/kolaente)
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2022-2023 The go-mail Authors
|
||||
SPDX-FileCopyrightText: The go-mail Authors
|
||||
|
||||
SPDX-License-Identifier: CC0-1.0
|
||||
SPDX-License-Identifier: MIT
|
||||
-->
|
||||
|
||||
# Security Policy
|
||||
|
||||
+196
-13
@@ -1,46 +1,229 @@
|
||||
// SPDX-FileCopyrightText: 2022-2023 The go-mail Authors
|
||||
// SPDX-FileCopyrightText: The go-mail Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package mail
|
||||
|
||||
import "errors"
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SMTPAuthType represents a string to any SMTP AUTH type
|
||||
// SMTPAuthType is a type wrapper for a string type. It represents the type of SMTP authentication
|
||||
// mechanism to be used.
|
||||
type SMTPAuthType string
|
||||
|
||||
// Supported SMTP AUTH types
|
||||
const (
|
||||
// SMTPAuthCramMD5 is the "CRAM-MD5" SASL authentication mechanism as described in RFC 4954
|
||||
// SMTPAuthCramMD5 is the "CRAM-MD5" SASL authentication mechanism as described in RFC 4954.
|
||||
// https://datatracker.ietf.org/doc/html/rfc4954/
|
||||
//
|
||||
// CRAM-MD5 is not secure by modern standards. The vulnerabilities of MD5 and the lack of
|
||||
// advanced security features make it inappropriate for protecting sensitive communications
|
||||
// today.
|
||||
//
|
||||
// It was recommended to deprecate the standard in 20 November 2008. As an alternative it
|
||||
// recommends e.g. SCRAM or SASL Plain protected by TLS instead.
|
||||
//
|
||||
// https://datatracker.ietf.org/doc/html/draft-ietf-sasl-crammd5-to-historic-00.html
|
||||
SMTPAuthCramMD5 SMTPAuthType = "CRAM-MD5"
|
||||
|
||||
// SMTPAuthLogin is the "LOGIN" SASL authentication mechanism
|
||||
// SMTPAuthCustom is a custom SMTP AUTH mechanism provided by the user. If a user provides
|
||||
// a custom smtp.Auth function to the Client, the Client will its smtpAuthType to this type.
|
||||
//
|
||||
// Do not use this SMTPAuthType without setting a custom smtp.Auth function on the Client.
|
||||
SMTPAuthCustom SMTPAuthType = "CUSTOM"
|
||||
|
||||
// SMTPAuthLogin is the "LOGIN" SASL authentication mechanism. This authentication mechanism
|
||||
// does not have an official RFC that could be followed. There is a spec by Microsoft and an
|
||||
// IETF draft. The IETF draft is more lax than the MS spec, therefore we follow the I-D, which
|
||||
// automatically matches the MS spec.
|
||||
//
|
||||
// Since the "LOGIN" SASL authentication mechanism transmits the username and password in
|
||||
// plaintext over the internet connection, we only allow this mechanism over a TLS secured
|
||||
// connection.
|
||||
//
|
||||
// https://msopenspecs.azureedge.net/files/MS-XLOGIN/%5bMS-XLOGIN%5d.pdf
|
||||
//
|
||||
// https://datatracker.ietf.org/doc/html/draft-murchison-sasl-login-00
|
||||
SMTPAuthLogin SMTPAuthType = "LOGIN"
|
||||
|
||||
// SMTPAuthLoginNoEnc is the "LOGIN" SASL authentication mechanism. This authentication mechanism
|
||||
// does not have an official RFC that could be followed. There is a spec by Microsoft and an
|
||||
// IETF draft. The IETF draft is more lax than the MS spec, therefore we follow the I-D, which
|
||||
// automatically matches the MS spec.
|
||||
//
|
||||
// Since the "LOGIN" SASL authentication mechanism transmits the username and password in
|
||||
// plaintext over the internet connection, by default we only allow this mechanism over
|
||||
// a TLS secured connection. This authentiation mechanism overrides this default and will
|
||||
// allow LOGIN authentication via an unencrypted channel. This can be useful if the
|
||||
// connection has already been secured in a different way (e. g. a SSH tunnel)
|
||||
//
|
||||
// Note: Use this authentication method with caution. If used in the wrong way, you might
|
||||
// expose your authentication information over unencrypted channels!
|
||||
//
|
||||
// https://msopenspecs.azureedge.net/files/MS-XLOGIN/%5bMS-XLOGIN%5d.pdf
|
||||
//
|
||||
// https://datatracker.ietf.org/doc/html/draft-murchison-sasl-login-00
|
||||
SMTPAuthLoginNoEnc SMTPAuthType = "LOGIN-NOENC"
|
||||
|
||||
// SMTPAuthNoAuth is equivalent to performing no authentication at all. It is a convenience
|
||||
// option and should not be used. Instead, for mail servers that do no support/require
|
||||
// authentication, the Client should not be used with the WithSMTPAuth option
|
||||
SMTPAuthNoAuth SMTPAuthType = ""
|
||||
// authentication, the Client should not be passed the WithSMTPAuth option at all.
|
||||
SMTPAuthNoAuth SMTPAuthType = "NOAUTH"
|
||||
|
||||
// SMTPAuthPlain is the "PLAIN" authentication mechanism as described in RFC 4616
|
||||
// SMTPAuthPlain is the "PLAIN" authentication mechanism as described in RFC 4616.
|
||||
//
|
||||
// Since the "PLAIN" SASL authentication mechanism transmits the username and password in
|
||||
// plaintext over the internet connection, we only allow this mechanism over a TLS secured
|
||||
// connection.
|
||||
//
|
||||
// https://datatracker.ietf.org/doc/html/rfc4616/
|
||||
SMTPAuthPlain SMTPAuthType = "PLAIN"
|
||||
|
||||
// SMTPAuthPlainNoEnc is the "PLAIN" authentication mechanism as described in RFC 4616.
|
||||
//
|
||||
// Since the "PLAIN" SASL authentication mechanism transmits the username and password in
|
||||
// plaintext over the internet connection, by default we only allow this mechanism over
|
||||
// a TLS secured connection. This authentiation mechanism overrides this default and will
|
||||
// allow PLAIN authentication via an unencrypted channel. This can be useful if the
|
||||
// connection has already been secured in a different way (e. g. a SSH tunnel)
|
||||
//
|
||||
// Note: Use this authentication method with caution. If used in the wrong way, you might
|
||||
// expose your authentication information over unencrypted channels!
|
||||
//
|
||||
// https://datatracker.ietf.org/doc/html/rfc4616/
|
||||
SMTPAuthPlainNoEnc SMTPAuthType = "PLAIN-NOENC"
|
||||
|
||||
// SMTPAuthXOAUTH2 is the "XOAUTH2" SASL authentication mechanism.
|
||||
// https://developers.google.com/gmail/imap/xoauth2-protocol
|
||||
SMTPAuthXOAUTH2 SMTPAuthType = "XOAUTH2"
|
||||
|
||||
// SMTPAuthSCRAMSHA1 is the "SCRAM-SHA-1" SASL authentication mechanism as described in RFC 5802.
|
||||
//
|
||||
// SCRAM-SHA-1 is still considered secure for certain applications, particularly when used as part
|
||||
// of a challenge-response authentication mechanism (as we use it). However, it is generally
|
||||
// recommended to prefer stronger alternatives like SCRAM-SHA-256(-PLUS), as SHA-1 has known
|
||||
// vulnerabilities in other contexts, although it remains effective in HMAC constructions.
|
||||
//
|
||||
// https://datatracker.ietf.org/doc/html/rfc5802
|
||||
SMTPAuthSCRAMSHA1 SMTPAuthType = "SCRAM-SHA-1"
|
||||
|
||||
// SMTPAuthSCRAMSHA1PLUS is the "SCRAM-SHA-1-PLUS" SASL authentication mechanism as described in RFC 5802.
|
||||
//
|
||||
// SCRAM-SHA-X-PLUS authentication require TLS channel bindings to protect against MitM attacks and
|
||||
// to guarantee that the integrity of the transport layer is preserved throughout the authentication
|
||||
// process. Therefore we only allow this mechanism over a TLS secured connection.
|
||||
//
|
||||
// SCRAM-SHA-1-PLUS is still considered secure for certain applications, particularly when used as part
|
||||
// of a challenge-response authentication mechanism (as we use it). However, it is generally
|
||||
// recommended to prefer stronger alternatives like SCRAM-SHA-256(-PLUS), as SHA-1 has known
|
||||
// vulnerabilities in other contexts, although it remains effective in HMAC constructions.
|
||||
//
|
||||
// https://datatracker.ietf.org/doc/html/rfc5802
|
||||
SMTPAuthSCRAMSHA1PLUS SMTPAuthType = "SCRAM-SHA-1-PLUS"
|
||||
|
||||
// SMTPAuthSCRAMSHA256 is the "SCRAM-SHA-256" SASL authentication mechanism as described in RFC 7677.
|
||||
//
|
||||
// https://datatracker.ietf.org/doc/html/rfc7677
|
||||
SMTPAuthSCRAMSHA256 SMTPAuthType = "SCRAM-SHA-256"
|
||||
|
||||
// SMTPAuthSCRAMSHA256PLUS is the "SCRAM-SHA-256-PLUS" SASL authentication mechanism as described in RFC 7677.
|
||||
//
|
||||
// SCRAM-SHA-X-PLUS authentication require TLS channel bindings to protect against MitM attacks and
|
||||
// to guarantee that the integrity of the transport layer is preserved throughout the authentication
|
||||
// process. Therefore we only allow this mechanism over a TLS secured connection.
|
||||
//
|
||||
// https://datatracker.ietf.org/doc/html/rfc7677
|
||||
SMTPAuthSCRAMSHA256PLUS SMTPAuthType = "SCRAM-SHA-256-PLUS"
|
||||
|
||||
// SMTPAuthAutoDiscover is a mechanism that dynamically discovers all authentication mechanisms
|
||||
// supported by the SMTP server and selects the strongest available one.
|
||||
//
|
||||
// This type simplifies authentication by automatically negotiating the most secure mechanism
|
||||
// offered by the server, based on a predefined security ranking. For instance, mechanisms like
|
||||
// SCRAM-SHA-256(-PLUS) or XOAUTH2 are prioritized over weaker mechanisms such as CRAM-MD5 or PLAIN.
|
||||
//
|
||||
// The negotiation process ensures that mechanisms requiring additional capabilities (e.g.,
|
||||
// SCRAM-SHA-X-PLUS with TLS channel binding) are only selected when the necessary prerequisites
|
||||
// are in place, such as an active TLS-secured connection.
|
||||
//
|
||||
// By automating mechanism selection, SMTPAuthAutoDiscover minimizes configuration effort while
|
||||
// maximizing security and compatibility with a wide range of SMTP servers.
|
||||
SMTPAuthAutoDiscover SMTPAuthType = "AUTODISCOVER"
|
||||
)
|
||||
|
||||
// SMTP Auth related static errors
|
||||
var (
|
||||
// ErrPlainAuthNotSupported should be used if the target server does not support the "PLAIN" schema
|
||||
// ErrPlainAuthNotSupported is returned when the server does not support the "PLAIN" SMTP
|
||||
// authentication type.
|
||||
ErrPlainAuthNotSupported = errors.New("server does not support SMTP AUTH type: PLAIN")
|
||||
|
||||
// ErrLoginAuthNotSupported should be used if the target server does not support the "LOGIN" schema
|
||||
// ErrLoginAuthNotSupported is returned when the server does not support the "LOGIN" SMTP
|
||||
// authentication type.
|
||||
ErrLoginAuthNotSupported = errors.New("server does not support SMTP AUTH type: LOGIN")
|
||||
|
||||
// ErrCramMD5AuthNotSupported should be used if the target server does not support the "CRAM-MD5" schema
|
||||
// ErrCramMD5AuthNotSupported is returned when the server does not support the "CRAM-MD5" SMTP
|
||||
// authentication type.
|
||||
ErrCramMD5AuthNotSupported = errors.New("server does not support SMTP AUTH type: CRAM-MD5")
|
||||
|
||||
// ErrXOauth2AuthNotSupported should be used if the target server does not support the "XOAUTH2" schema
|
||||
// ErrXOauth2AuthNotSupported is returned when the server does not support the "XOAUTH2" schema.
|
||||
ErrXOauth2AuthNotSupported = errors.New("server does not support SMTP AUTH type: XOAUTH2")
|
||||
|
||||
// ErrSCRAMSHA1AuthNotSupported is returned when the server does not support the "SCRAM-SHA-1" SMTP
|
||||
// authentication type.
|
||||
ErrSCRAMSHA1AuthNotSupported = errors.New("server does not support SMTP AUTH type: SCRAM-SHA-1")
|
||||
|
||||
// ErrSCRAMSHA1PLUSAuthNotSupported is returned when the server does not support the "SCRAM-SHA-1-PLUS" SMTP
|
||||
// authentication type.
|
||||
ErrSCRAMSHA1PLUSAuthNotSupported = errors.New("server does not support SMTP AUTH type: SCRAM-SHA-1-PLUS")
|
||||
|
||||
// ErrSCRAMSHA256AuthNotSupported is returned when the server does not support the "SCRAM-SHA-256" SMTP
|
||||
// authentication type.
|
||||
ErrSCRAMSHA256AuthNotSupported = errors.New("server does not support SMTP AUTH type: SCRAM-SHA-256")
|
||||
|
||||
// ErrSCRAMSHA256PLUSAuthNotSupported is returned when the server does not support the "SCRAM-SHA-256-PLUS" SMTP
|
||||
// authentication type.
|
||||
ErrSCRAMSHA256PLUSAuthNotSupported = errors.New("server does not support SMTP AUTH type: SCRAM-SHA-256-PLUS")
|
||||
|
||||
// ErrNoSupportedAuthDiscovered is returned when the SMTP Auth AutoDiscover process fails to identify
|
||||
// any supported authentication mechanisms offered by the server.
|
||||
ErrNoSupportedAuthDiscovered = errors.New("SMTP Auth autodiscover was not able to detect a supported " +
|
||||
"authentication mechanism")
|
||||
)
|
||||
|
||||
// UnmarshalString satisfies the fig.StringUnmarshaler interface for the SMTPAuthType type
|
||||
// https://pkg.go.dev/github.com/kkyr/fig#StringUnmarshaler
|
||||
func (sa *SMTPAuthType) UnmarshalString(value string) error {
|
||||
switch strings.ToLower(value) {
|
||||
case "auto", "autodiscover", "autodiscovery":
|
||||
*sa = SMTPAuthAutoDiscover
|
||||
case "cram-md5", "crammd5", "cram":
|
||||
*sa = SMTPAuthCramMD5
|
||||
case "custom":
|
||||
*sa = SMTPAuthCustom
|
||||
case "login":
|
||||
*sa = SMTPAuthLogin
|
||||
case "login-noenc":
|
||||
*sa = SMTPAuthLoginNoEnc
|
||||
case "none", "noauth", "no":
|
||||
*sa = SMTPAuthNoAuth
|
||||
case "plain":
|
||||
*sa = SMTPAuthPlain
|
||||
case "plain-noenc":
|
||||
*sa = SMTPAuthPlainNoEnc
|
||||
case "scram-sha-1", "scram-sha1", "scramsha1":
|
||||
*sa = SMTPAuthSCRAMSHA1
|
||||
case "scram-sha-1-plus", "scram-sha1-plus", "scramsha1plus":
|
||||
*sa = SMTPAuthSCRAMSHA1PLUS
|
||||
case "scram-sha-256", "scram-sha256", "scramsha256":
|
||||
*sa = SMTPAuthSCRAMSHA256
|
||||
case "scram-sha-256-plus", "scram-sha256-plus", "scramsha256plus":
|
||||
*sa = SMTPAuthSCRAMSHA256PLUS
|
||||
case "xoauth2", "oauth2":
|
||||
*sa = SMTPAuthXOAUTH2
|
||||
default:
|
||||
return fmt.Errorf("unsupported SMTP auth type: %s", value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
+49
-25
@@ -1,33 +1,48 @@
|
||||
// SPDX-FileCopyrightText: 2022-2023 The go-mail Authors
|
||||
// SPDX-FileCopyrightText: The go-mail Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package mail
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
// ErrNoOutWriter is an error message that should be used if a Base64LineBreaker has no out io.Writer set
|
||||
const ErrNoOutWriter = "no io.Writer set for Base64LineBreaker"
|
||||
// newlineBytes is a byte slice representation of the SingleNewLine constant used for line breaking
|
||||
// in encoding processes.
|
||||
var newlineBytes = []byte(SingleNewLine)
|
||||
|
||||
// Base64LineBreaker is a io.WriteCloser that writes Base64 encoded data streams
|
||||
// with line breaks at a given line length
|
||||
type Base64LineBreaker struct {
|
||||
// base64LineBreaker handles base64 encoding with the insertion of new lines after a certain number
|
||||
// of characters.
|
||||
//
|
||||
// This struct is used to manage base64 encoding while ensuring that new lines are inserted after
|
||||
// reaching a specific line length. It satisfies the io.WriteCloser interface.
|
||||
//
|
||||
// References:
|
||||
// - https://datatracker.ietf.org/doc/html/rfc2045 (Base64 and line length limitations)
|
||||
type base64LineBreaker struct {
|
||||
line [MaxBodyLength]byte
|
||||
used int
|
||||
out io.Writer
|
||||
}
|
||||
|
||||
var newlineBytes = []byte(SingleNewLine)
|
||||
|
||||
// Write writes the data stream and inserts a SingleNewLine when the maximum
|
||||
// line length is reached
|
||||
func (l *Base64LineBreaker) Write(data []byte) (numBytes int, err error) {
|
||||
// Write writes data to the base64LineBreaker, ensuring lines do not exceed MaxBodyLength.
|
||||
//
|
||||
// This method writes the provided data to the base64LineBreaker. It ensures that the written
|
||||
// lines do not exceed the MaxBodyLength. If the data exceeds the limit, it handles the
|
||||
// continuation by splitting the data and writing new lines as necessary.
|
||||
//
|
||||
// Parameters:
|
||||
// - data: A byte slice containing the data to be written.
|
||||
//
|
||||
// Returns:
|
||||
// - numBytes: The number of bytes written.
|
||||
// - err: An error if one occurred during the write operation.
|
||||
func (l *base64LineBreaker) Write(data []byte) (numBytes int, err error) {
|
||||
if l.out == nil {
|
||||
err = fmt.Errorf(ErrNoOutWriter)
|
||||
return
|
||||
err = errors.New("no io.Writer set for base64LineBreaker")
|
||||
return numBytes, err
|
||||
}
|
||||
if l.used+len(data) < MaxBodyLength {
|
||||
copy(l.line[l.used:], data)
|
||||
@@ -35,36 +50,45 @@ func (l *Base64LineBreaker) Write(data []byte) (numBytes int, err error) {
|
||||
return len(data), nil
|
||||
}
|
||||
|
||||
numBytes, err = l.out.Write(l.line[0:l.used])
|
||||
_, err = l.out.Write(l.line[0:l.used])
|
||||
if err != nil {
|
||||
return
|
||||
return numBytes, err
|
||||
}
|
||||
excess := MaxBodyLength - l.used
|
||||
l.used = 0
|
||||
|
||||
numBytes, err = l.out.Write(data[0:excess])
|
||||
if err != nil {
|
||||
return
|
||||
return numBytes, err
|
||||
}
|
||||
|
||||
numBytes, err = l.out.Write(newlineBytes)
|
||||
_, err = l.out.Write(newlineBytes)
|
||||
if err != nil {
|
||||
return
|
||||
return numBytes, err
|
||||
}
|
||||
|
||||
return l.Write(data[excess:])
|
||||
var n int
|
||||
n, err = l.Write(data[excess:]) // recurse
|
||||
numBytes += n
|
||||
return numBytes, err
|
||||
}
|
||||
|
||||
// Close closes the Base64LineBreaker and writes any access data that is still
|
||||
// unwritten in memory
|
||||
func (l *Base64LineBreaker) Close() (err error) {
|
||||
// Close finalizes the base64LineBreaker, writing any remaining buffered data and appending a newline.
|
||||
//
|
||||
// This method ensures that any remaining data in the buffer is written to the output and appends
|
||||
// a newline. It is used to finalize the base64LineBreaker and should be called when no more data
|
||||
// is expected to be written.
|
||||
//
|
||||
// Returns:
|
||||
// - err: An error if one occurred during the final write operation.
|
||||
func (l *base64LineBreaker) Close() (err error) {
|
||||
if l.used > 0 {
|
||||
_, err = l.out.Write(l.line[0:l.used])
|
||||
if err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
_, err = l.out.Write(newlineBytes)
|
||||
}
|
||||
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
+1257
-372
File diff suppressed because it is too large
Load Diff
-134
@@ -1,134 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2022-2023 The go-mail Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//go:build !go1.20
|
||||
// +build !go1.20
|
||||
|
||||
package mail
|
||||
|
||||
import "strings"
|
||||
|
||||
// Send sends out the mail message
|
||||
func (c *Client) Send(messages ...*Msg) error {
|
||||
if cerr := c.checkConn(); cerr != nil {
|
||||
return &SendError{Reason: ErrConnCheck, errlist: []error{cerr}, isTemp: isTempError(cerr)}
|
||||
}
|
||||
var errs []*SendError
|
||||
for _, message := range messages {
|
||||
message.sendError = nil
|
||||
if message.encoding == NoEncoding {
|
||||
if ok, _ := c.smtpClient.Extension("8BITMIME"); !ok {
|
||||
sendErr := &SendError{Reason: ErrNoUnencoded, isTemp: false}
|
||||
message.sendError = sendErr
|
||||
errs = append(errs, sendErr)
|
||||
continue
|
||||
}
|
||||
}
|
||||
from, err := message.GetSender(false)
|
||||
if err != nil {
|
||||
sendErr := &SendError{Reason: ErrGetSender, errlist: []error{err}, isTemp: isTempError(err)}
|
||||
message.sendError = sendErr
|
||||
errs = append(errs, sendErr)
|
||||
continue
|
||||
}
|
||||
rcpts, err := message.GetRecipients()
|
||||
if err != nil {
|
||||
sendErr := &SendError{Reason: ErrGetRcpts, errlist: []error{err}, isTemp: isTempError(err)}
|
||||
message.sendError = sendErr
|
||||
errs = append(errs, sendErr)
|
||||
continue
|
||||
}
|
||||
|
||||
if c.dsn {
|
||||
if c.dsnmrtype != "" {
|
||||
c.smtpClient.SetDSNMailReturnOption(string(c.dsnmrtype))
|
||||
}
|
||||
}
|
||||
if err = c.smtpClient.Mail(from); err != nil {
|
||||
sendErr := &SendError{Reason: ErrSMTPMailFrom, errlist: []error{err}, isTemp: isTempError(err)}
|
||||
if resetSendErr := c.smtpClient.Reset(); resetSendErr != nil {
|
||||
sendErr.errlist = append(sendErr.errlist, resetSendErr)
|
||||
}
|
||||
message.sendError = sendErr
|
||||
errs = append(errs, sendErr)
|
||||
continue
|
||||
}
|
||||
failed := false
|
||||
rcptSendErr := &SendError{}
|
||||
rcptSendErr.errlist = make([]error, 0)
|
||||
rcptSendErr.rcpt = make([]string, 0)
|
||||
rcptNotifyOpt := strings.Join(c.dsnrntype, ",")
|
||||
c.smtpClient.SetDSNRcptNotifyOption(rcptNotifyOpt)
|
||||
for _, rcpt := range rcpts {
|
||||
if err = c.smtpClient.Rcpt(rcpt); err != nil {
|
||||
rcptSendErr.Reason = ErrSMTPRcptTo
|
||||
rcptSendErr.errlist = append(rcptSendErr.errlist, err)
|
||||
rcptSendErr.rcpt = append(rcptSendErr.rcpt, rcpt)
|
||||
rcptSendErr.isTemp = isTempError(err)
|
||||
failed = true
|
||||
}
|
||||
}
|
||||
if failed {
|
||||
if resetSendErr := c.smtpClient.Reset(); resetSendErr != nil {
|
||||
rcptSendErr.errlist = append(rcptSendErr.errlist, err)
|
||||
}
|
||||
message.sendError = rcptSendErr
|
||||
errs = append(errs, rcptSendErr)
|
||||
continue
|
||||
}
|
||||
writer, err := c.smtpClient.Data()
|
||||
if err != nil {
|
||||
sendErr := &SendError{Reason: ErrSMTPData, errlist: []error{err}, isTemp: isTempError(err)}
|
||||
message.sendError = sendErr
|
||||
errs = append(errs, sendErr)
|
||||
continue
|
||||
}
|
||||
_, err = message.WriteTo(writer)
|
||||
if err != nil {
|
||||
sendErr := &SendError{Reason: ErrWriteContent, errlist: []error{err}, isTemp: isTempError(err)}
|
||||
message.sendError = sendErr
|
||||
errs = append(errs, sendErr)
|
||||
continue
|
||||
}
|
||||
message.isDelivered = true
|
||||
|
||||
if err = writer.Close(); err != nil {
|
||||
sendErr := &SendError{Reason: ErrSMTPDataClose, errlist: []error{err}, isTemp: isTempError(err)}
|
||||
message.sendError = sendErr
|
||||
errs = append(errs, sendErr)
|
||||
continue
|
||||
}
|
||||
|
||||
if err = c.Reset(); err != nil {
|
||||
sendErr := &SendError{Reason: ErrSMTPReset, errlist: []error{err}, isTemp: isTempError(err)}
|
||||
message.sendError = sendErr
|
||||
errs = append(errs, sendErr)
|
||||
continue
|
||||
}
|
||||
if err = c.checkConn(); err != nil {
|
||||
sendErr := &SendError{Reason: ErrConnCheck, errlist: []error{err}, isTemp: isTempError(err)}
|
||||
message.sendError = sendErr
|
||||
errs = append(errs, sendErr)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
if len(errs) > 1 {
|
||||
returnErr := &SendError{Reason: ErrAmbiguous}
|
||||
for i := range errs {
|
||||
returnErr.errlist = append(returnErr.errlist, errs[i].errlist...)
|
||||
returnErr.rcpt = append(returnErr.rcpt, errs[i].rcpt...)
|
||||
}
|
||||
|
||||
// We assume that the isTemp flag from the last error we received should be the
|
||||
// indicator for the returned isTemp flag as well
|
||||
returnErr.isTemp = errs[len(errs)-1].isTemp
|
||||
|
||||
return returnErr
|
||||
}
|
||||
return errs[0]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
-111
@@ -1,111 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2022-2023 The go-mail Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//go:build go1.20
|
||||
// +build go1.20
|
||||
|
||||
package mail
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Send sends out the mail message
|
||||
func (c *Client) Send(messages ...*Msg) (returnErr error) {
|
||||
if err := c.checkConn(); err != nil {
|
||||
returnErr = &SendError{Reason: ErrConnCheck, errlist: []error{err}, isTemp: isTempError(err)}
|
||||
return
|
||||
}
|
||||
for _, message := range messages {
|
||||
message.sendError = nil
|
||||
if message.encoding == NoEncoding {
|
||||
if ok, _ := c.smtpClient.Extension("8BITMIME"); !ok {
|
||||
message.sendError = &SendError{Reason: ErrNoUnencoded, isTemp: false}
|
||||
returnErr = errors.Join(returnErr, message.sendError)
|
||||
continue
|
||||
}
|
||||
}
|
||||
from, err := message.GetSender(false)
|
||||
if err != nil {
|
||||
message.sendError = &SendError{Reason: ErrGetSender, errlist: []error{err}, isTemp: isTempError(err)}
|
||||
returnErr = errors.Join(returnErr, message.sendError)
|
||||
continue
|
||||
}
|
||||
rcpts, err := message.GetRecipients()
|
||||
if err != nil {
|
||||
message.sendError = &SendError{Reason: ErrGetRcpts, errlist: []error{err}, isTemp: isTempError(err)}
|
||||
returnErr = errors.Join(returnErr, message.sendError)
|
||||
continue
|
||||
}
|
||||
|
||||
if c.dsn {
|
||||
if c.dsnmrtype != "" {
|
||||
c.smtpClient.SetDSNMailReturnOption(string(c.dsnmrtype))
|
||||
}
|
||||
}
|
||||
if err = c.smtpClient.Mail(from); err != nil {
|
||||
message.sendError = &SendError{Reason: ErrSMTPMailFrom, errlist: []error{err}, isTemp: isTempError(err)}
|
||||
returnErr = errors.Join(returnErr, message.sendError)
|
||||
if resetSendErr := c.smtpClient.Reset(); resetSendErr != nil {
|
||||
returnErr = errors.Join(returnErr, resetSendErr)
|
||||
}
|
||||
continue
|
||||
}
|
||||
failed := false
|
||||
rcptSendErr := &SendError{}
|
||||
rcptSendErr.errlist = make([]error, 0)
|
||||
rcptSendErr.rcpt = make([]string, 0)
|
||||
rcptNotifyOpt := strings.Join(c.dsnrntype, ",")
|
||||
c.smtpClient.SetDSNRcptNotifyOption(rcptNotifyOpt)
|
||||
for _, rcpt := range rcpts {
|
||||
if err = c.smtpClient.Rcpt(rcpt); err != nil {
|
||||
rcptSendErr.Reason = ErrSMTPRcptTo
|
||||
rcptSendErr.errlist = append(rcptSendErr.errlist, err)
|
||||
rcptSendErr.rcpt = append(rcptSendErr.rcpt, rcpt)
|
||||
rcptSendErr.isTemp = isTempError(err)
|
||||
failed = true
|
||||
}
|
||||
}
|
||||
if failed {
|
||||
if resetSendErr := c.smtpClient.Reset(); resetSendErr != nil {
|
||||
returnErr = errors.Join(returnErr, resetSendErr)
|
||||
}
|
||||
message.sendError = rcptSendErr
|
||||
returnErr = errors.Join(returnErr, message.sendError)
|
||||
continue
|
||||
}
|
||||
writer, err := c.smtpClient.Data()
|
||||
if err != nil {
|
||||
message.sendError = &SendError{Reason: ErrSMTPData, errlist: []error{err}, isTemp: isTempError(err)}
|
||||
returnErr = errors.Join(returnErr, message.sendError)
|
||||
continue
|
||||
}
|
||||
_, err = message.WriteTo(writer)
|
||||
if err != nil {
|
||||
message.sendError = &SendError{Reason: ErrWriteContent, errlist: []error{err}, isTemp: isTempError(err)}
|
||||
returnErr = errors.Join(returnErr, message.sendError)
|
||||
continue
|
||||
}
|
||||
message.isDelivered = true
|
||||
|
||||
if err = writer.Close(); err != nil {
|
||||
message.sendError = &SendError{Reason: ErrSMTPDataClose, errlist: []error{err}, isTemp: isTempError(err)}
|
||||
returnErr = errors.Join(returnErr, message.sendError)
|
||||
continue
|
||||
}
|
||||
|
||||
if err = c.Reset(); err != nil {
|
||||
message.sendError = &SendError{Reason: ErrSMTPReset, errlist: []error{err}, isTemp: isTempError(err)}
|
||||
returnErr = errors.Join(returnErr, message.sendError)
|
||||
continue
|
||||
}
|
||||
if err = c.checkConn(); err != nil {
|
||||
message.sendError = &SendError{Reason: ErrConnCheck, errlist: []error{err}, isTemp: isTempError(err)}
|
||||
returnErr = errors.Join(returnErr, message.sendError)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
+6
-6
@@ -1,22 +1,22 @@
|
||||
# SPDX-FileCopyrightText: 2022-2023 The go-mail Authors
|
||||
# SPDX-FileCopyrightText: The go-mail Authors
|
||||
#
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
coverage:
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
target: 85%
|
||||
threshold: 5%
|
||||
target: 95%
|
||||
threshold: 2%
|
||||
base: auto
|
||||
if_ci_failed: error
|
||||
only_pulls: false
|
||||
patch:
|
||||
default:
|
||||
target: 80%
|
||||
target: 95%
|
||||
base: auto
|
||||
if_ci_failed: error
|
||||
threshold: 5%
|
||||
threshold: 2%
|
||||
|
||||
comment:
|
||||
require_changes: true
|
||||
|
||||
+9
-4
@@ -1,9 +1,14 @@
|
||||
// SPDX-FileCopyrightText: 2022-2023 The go-mail Authors
|
||||
// SPDX-FileCopyrightText: The go-mail Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Package mail provides a simple and easy way to send mails with Go
|
||||
// Package mail provides an easy to use interface for formating and sending mails. go-mail follows idiomatic Go style
|
||||
// and best practice. It has a small dependency footprint by mainly relying on the Go Standard Library and the Go
|
||||
// extended packages. It combines a lot of functionality from the standard library to give easy and convenient access
|
||||
// to mail and SMTP related tasks. It works like a programmatic email client and provides lots of methods and
|
||||
// functionalities you would consider standard in a MUA.
|
||||
package mail
|
||||
|
||||
// VERSION is used in the default user agent string
|
||||
const VERSION = "0.4.1"
|
||||
// VERSION indicates the current version of the package. It is also attached to the default user
|
||||
// agent string.
|
||||
const VERSION = "0.7.1"
|
||||
|
||||
+134
-58
@@ -1,163 +1,239 @@
|
||||
// SPDX-FileCopyrightText: 2022-2023 The go-mail Authors
|
||||
// SPDX-FileCopyrightText: The go-mail Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package mail
|
||||
|
||||
// Charset represents a character set for the encoding
|
||||
// Charset is a type wrapper for a string representing different character encodings.
|
||||
type Charset string
|
||||
|
||||
// ContentType represents a content type for the Msg
|
||||
// ContentType is a type wrapper for a string and represents the MIME type of the content being handled.
|
||||
type ContentType string
|
||||
|
||||
// Encoding represents a MIME encoding scheme like quoted-printable or Base64.
|
||||
// Encoding is a type wrapper for a string and represents the type of encoding used for email messages
|
||||
// and/or parts.
|
||||
type Encoding string
|
||||
|
||||
// MIMEVersion represents the MIME version for the mail
|
||||
// MIMEVersion is a type wrapper for a string nad represents the MIME version used in email messages.
|
||||
type MIMEVersion string
|
||||
|
||||
// MIMEType represents the MIME type for the mail
|
||||
// MIMEType is a type wrapper for a string and represents the MIME type for the Msg content or parts.
|
||||
type MIMEType string
|
||||
|
||||
// List of supported encodings
|
||||
const (
|
||||
// EncodingB64 represents the Base64 encoding as specified in RFC 2045.
|
||||
//
|
||||
// https://datatracker.ietf.org/doc/html/rfc2045#section-6.8
|
||||
EncodingB64 Encoding = "base64"
|
||||
|
||||
// EncodingQP represents the "quoted-printable" encoding as specified in RFC 2045.
|
||||
//
|
||||
// https://datatracker.ietf.org/doc/html/rfc2045#section-6.7
|
||||
EncodingQP Encoding = "quoted-printable"
|
||||
|
||||
// NoEncoding avoids any character encoding (except of the mail headers)
|
||||
// EncodingUSASCII represents encoding with only US-ASCII characters (aka 7Bit)
|
||||
//
|
||||
// https://datatracker.ietf.org/doc/html/rfc2045#section-2.7
|
||||
EncodingUSASCII Encoding = "7bit"
|
||||
|
||||
// NoEncoding represents 8-bit encoding for email messages as specified in RFC 6152.
|
||||
//
|
||||
// https://datatracker.ietf.org/doc/html/rfc2045#section-2.8
|
||||
//
|
||||
// https://datatracker.ietf.org/doc/html/rfc6152
|
||||
NoEncoding Encoding = "8bit"
|
||||
)
|
||||
|
||||
// List of common charsets
|
||||
const (
|
||||
// CharsetUTF7 represents the "UTF-7" charset
|
||||
// CharsetUTF7 represents the "UTF-7" charset.
|
||||
CharsetUTF7 Charset = "UTF-7"
|
||||
|
||||
// CharsetUTF8 represents the "UTF-8" charset
|
||||
// CharsetUTF8 represents the "UTF-8" charset.
|
||||
CharsetUTF8 Charset = "UTF-8"
|
||||
|
||||
// CharsetASCII represents the "US-ASCII" charset
|
||||
// CharsetASCII represents the "US-ASCII" charset.
|
||||
CharsetASCII Charset = "US-ASCII"
|
||||
|
||||
// CharsetISO88591 represents the "ISO-8859-1" charset
|
||||
// CharsetISO88591 represents the "ISO-8859-1" charset.
|
||||
CharsetISO88591 Charset = "ISO-8859-1"
|
||||
|
||||
// CharsetISO88592 represents the "ISO-8859-2" charset
|
||||
// CharsetISO88592 represents the "ISO-8859-2" charset.
|
||||
CharsetISO88592 Charset = "ISO-8859-2"
|
||||
|
||||
// CharsetISO88593 represents the "ISO-8859-3" charset
|
||||
// CharsetISO88593 represents the "ISO-8859-3" charset.
|
||||
CharsetISO88593 Charset = "ISO-8859-3"
|
||||
|
||||
// CharsetISO88594 represents the "ISO-8859-4" charset
|
||||
// CharsetISO88594 represents the "ISO-8859-4" charset.
|
||||
CharsetISO88594 Charset = "ISO-8859-4"
|
||||
|
||||
// CharsetISO88595 represents the "ISO-8859-5" charset
|
||||
// CharsetISO88595 represents the "ISO-8859-5" charset.
|
||||
CharsetISO88595 Charset = "ISO-8859-5"
|
||||
|
||||
// CharsetISO88596 represents the "ISO-8859-6" charset
|
||||
// CharsetISO88596 represents the "ISO-8859-6" charset.
|
||||
CharsetISO88596 Charset = "ISO-8859-6"
|
||||
|
||||
// CharsetISO88597 represents the "ISO-8859-7" charset
|
||||
// CharsetISO88597 represents the "ISO-8859-7" charset.
|
||||
CharsetISO88597 Charset = "ISO-8859-7"
|
||||
|
||||
// CharsetISO88599 represents the "ISO-8859-9" charset
|
||||
// CharsetISO88599 represents the "ISO-8859-9" charset.
|
||||
CharsetISO88599 Charset = "ISO-8859-9"
|
||||
|
||||
// CharsetISO885913 represents the "ISO-8859-13" charset
|
||||
// CharsetISO885913 represents the "ISO-8859-13" charset.
|
||||
CharsetISO885913 Charset = "ISO-8859-13"
|
||||
|
||||
// CharsetISO885914 represents the "ISO-8859-14" charset
|
||||
// CharsetISO885914 represents the "ISO-8859-14" charset.
|
||||
CharsetISO885914 Charset = "ISO-8859-14"
|
||||
|
||||
// CharsetISO885915 represents the "ISO-8859-15" charset
|
||||
// CharsetISO885915 represents the "ISO-8859-15" charset.
|
||||
CharsetISO885915 Charset = "ISO-8859-15"
|
||||
|
||||
// CharsetISO885916 represents the "ISO-8859-16" charset
|
||||
// CharsetISO885916 represents the "ISO-8859-16" charset.
|
||||
CharsetISO885916 Charset = "ISO-8859-16"
|
||||
|
||||
// CharsetISO2022JP represents the "ISO-2022-JP" charset
|
||||
// CharsetISO2022JP represents the "ISO-2022-JP" charset.
|
||||
CharsetISO2022JP Charset = "ISO-2022-JP"
|
||||
|
||||
// CharsetISO2022KR represents the "ISO-2022-KR" charset
|
||||
// CharsetISO2022KR represents the "ISO-2022-KR" charset.
|
||||
CharsetISO2022KR Charset = "ISO-2022-KR"
|
||||
|
||||
// CharsetWindows1250 represents the "windows-1250" charset
|
||||
// CharsetWindows1250 represents the "windows-1250" charset.
|
||||
CharsetWindows1250 Charset = "windows-1250"
|
||||
|
||||
// CharsetWindows1251 represents the "windows-1251" charset
|
||||
// CharsetWindows1251 represents the "windows-1251" charset.
|
||||
CharsetWindows1251 Charset = "windows-1251"
|
||||
|
||||
// CharsetWindows1252 represents the "windows-1252" charset
|
||||
// CharsetWindows1252 represents the "windows-1252" charset.
|
||||
CharsetWindows1252 Charset = "windows-1252"
|
||||
|
||||
// CharsetWindows1255 represents the "windows-1255" charset
|
||||
// CharsetWindows1255 represents the "windows-1255" charset.
|
||||
CharsetWindows1255 Charset = "windows-1255"
|
||||
|
||||
// CharsetWindows1256 represents the "windows-1256" charset
|
||||
// CharsetWindows1256 represents the "windows-1256" charset.
|
||||
CharsetWindows1256 Charset = "windows-1256"
|
||||
|
||||
// CharsetKOI8R represents the "KOI8-R" charset
|
||||
// CharsetKOI8R represents the "KOI8-R" charset.
|
||||
CharsetKOI8R Charset = "KOI8-R"
|
||||
|
||||
// CharsetKOI8U represents the "KOI8-U" charset
|
||||
// CharsetKOI8U represents the "KOI8-U" charset.
|
||||
CharsetKOI8U Charset = "KOI8-U"
|
||||
|
||||
// CharsetBig5 represents the "Big5" charset
|
||||
// CharsetBig5 represents the "Big5" charset.
|
||||
CharsetBig5 Charset = "Big5"
|
||||
|
||||
// CharsetGB18030 represents the "GB18030" charset
|
||||
// CharsetGB18030 represents the "GB18030" charset.
|
||||
CharsetGB18030 Charset = "GB18030"
|
||||
|
||||
// CharsetGB2312 represents the "GB2312" charset
|
||||
// CharsetGB2312 represents the "GB2312" charset.
|
||||
CharsetGB2312 Charset = "GB2312"
|
||||
|
||||
// CharsetTIS620 represents the "TIS-620" charset
|
||||
// CharsetTIS620 represents the "TIS-620" charset.
|
||||
CharsetTIS620 Charset = "TIS-620"
|
||||
|
||||
// CharsetEUCKR represents the "EUC-KR" charset
|
||||
// CharsetEUCKR represents the "EUC-KR" charset.
|
||||
CharsetEUCKR Charset = "EUC-KR"
|
||||
|
||||
// CharsetShiftJIS represents the "Shift_JIS" charset
|
||||
// CharsetShiftJIS represents the "Shift_JIS" charset.
|
||||
CharsetShiftJIS Charset = "Shift_JIS"
|
||||
|
||||
// CharsetUnknown represents the "Unknown" charset
|
||||
// CharsetUnknown represents the "Unknown" charset.
|
||||
CharsetUnknown Charset = "Unknown"
|
||||
|
||||
// CharsetGBK represents the "GBK" charset
|
||||
// CharsetGBK represents the "GBK" charset.
|
||||
CharsetGBK Charset = "GBK"
|
||||
)
|
||||
|
||||
// List of MIME versions
|
||||
const (
|
||||
// Mime10 is the MIME Version 1.0
|
||||
Mime10 MIMEVersion = "1.0"
|
||||
)
|
||||
// MIME10 represents the MIME version "1.0" used in email messages.
|
||||
const MIME10 MIMEVersion = "1.0"
|
||||
|
||||
// List of common content types
|
||||
const (
|
||||
TypeTextPlain ContentType = "text/plain"
|
||||
TypeTextHTML ContentType = "text/html"
|
||||
// TypeAppOctetStream represents the MIME type for arbitrary binary data.
|
||||
TypeAppOctetStream ContentType = "application/octet-stream"
|
||||
TypePGPSignature ContentType = "application/pgp-signature"
|
||||
TypePGPEncrypted ContentType = "application/pgp-encrypted"
|
||||
|
||||
// TypeMultipartAlternative represents the MIME type for a message body that can contain multiple alternative
|
||||
// formats.
|
||||
TypeMultipartAlternative ContentType = "multipart/alternative"
|
||||
|
||||
// TypeMultipartMixed represents the MIME type for a multipart message containing different parts.
|
||||
TypeMultipartMixed ContentType = "multipart/mixed"
|
||||
|
||||
// TypeMultipartRelated represents the MIME type for a multipart message where each part is a related file
|
||||
// or resource.
|
||||
TypeMultipartRelated ContentType = "multipart/related"
|
||||
|
||||
// TypePGPSignature represents the MIME type for PGP signed messages.
|
||||
TypePGPSignature ContentType = "application/pgp-signature"
|
||||
|
||||
// TypePGPEncrypted represents the MIME type for PGP encrypted messages.
|
||||
TypePGPEncrypted ContentType = "application/pgp-encrypted"
|
||||
|
||||
// TypeTextHTML represents the MIME type for HTML text content.
|
||||
TypeTextHTML ContentType = "text/html"
|
||||
|
||||
// TypeTextPlain represents the MIME type for plain text content.
|
||||
TypeTextPlain ContentType = "text/plain"
|
||||
|
||||
// TypeSMIMESigned represents the MIME type for S/MIME singed messages.
|
||||
TypeSMIMESigned ContentType = `application/pkcs7-signature; name="smime.p7s"`
|
||||
)
|
||||
|
||||
// List of MIMETypes
|
||||
const (
|
||||
// MIMEAlternative MIMEType represents a MIME multipart/alternative type, used for emails with multiple versions.
|
||||
MIMEAlternative MIMEType = "alternative"
|
||||
MIMEMixed MIMEType = "mixed"
|
||||
MIMERelated MIMEType = "related"
|
||||
|
||||
// MIMEMixed MIMEType represents a MIME multipart/mixed type used fork emails containing different types of content.
|
||||
MIMEMixed MIMEType = "mixed"
|
||||
|
||||
// MIMERelated MIMEType represents a MIME multipart/related type, used for emails with related content entities.
|
||||
MIMERelated MIMEType = "related"
|
||||
|
||||
// MIMESMIMESigned MIMEType represents a MIME multipart/signed type, used for siging emails with S/MIME.
|
||||
MIMESMIMESigned MIMEType = `signed; protocol="application/pkcs7-signature"; micalg=sha-256`
|
||||
)
|
||||
|
||||
// String is a standard method to convert an Encoding into a printable format
|
||||
// String satisfies the fmt.Stringer interface for the Charset type.
|
||||
// It converts a Charset into a printable format.
|
||||
//
|
||||
// This method returns the string representation of the Charset, allowing it to be easily
|
||||
// printed or logged.
|
||||
//
|
||||
// Returns:
|
||||
// - A string representation of the Charset.
|
||||
func (c Charset) String() string {
|
||||
return string(c)
|
||||
}
|
||||
|
||||
// String satisfies the fmt.Stringer interface for the ContentType type.
|
||||
// It converts a ContentType into a printable format.
|
||||
//
|
||||
// This method returns the string representation of the ContentType, enabling its use
|
||||
// in formatted output such as logging or displaying information to the user.
|
||||
//
|
||||
// Returns:
|
||||
// - A string representation of the ContentType.
|
||||
func (c ContentType) String() string {
|
||||
return string(c)
|
||||
}
|
||||
|
||||
// String satisfies the fmt.Stringer interface for the Encoding type.
|
||||
// It converts an Encoding into a printable format.
|
||||
//
|
||||
// This method returns the string representation of the Encoding, which can be used
|
||||
// for displaying or logging purposes.
|
||||
//
|
||||
// Returns:
|
||||
// - A string representation of the Encoding.
|
||||
func (e Encoding) String() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
// String is a standard method to convert an Charset into a printable format
|
||||
func (c Charset) String() string {
|
||||
return string(c)
|
||||
// String satisfies the fmt.Stringer interface for the MIMEType type.
|
||||
// It converts an MIMEType into a printable format.
|
||||
//
|
||||
// This method returns the string representation of the MIMEType, which can be used
|
||||
// for displaying or logging purposes.
|
||||
//
|
||||
// Returns:
|
||||
// - A string representation of the MIMEType.
|
||||
func (e MIMEType) String() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
+87
-17
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: 2022-2023 The go-mail Authors
|
||||
// SPDX-FileCopyrightText: The go-mail Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
@@ -9,10 +9,15 @@ import (
|
||||
"net/textproto"
|
||||
)
|
||||
|
||||
// FileOption returns a function that can be used for grouping File options
|
||||
// FileOption is a function type used to modify properties of a File
|
||||
type FileOption func(*File)
|
||||
|
||||
// File is an attachment or embedded file of the Msg
|
||||
// File represents a file with properties such as content type, description, encoding, headers, name, and
|
||||
// a writer function.
|
||||
//
|
||||
// This struct can represent either an attachment or an embedded file in a Msg, and it stores relevant
|
||||
// metadata such as content type and encoding, as well as a function to write the file's content to an
|
||||
// io.Writer.
|
||||
type File struct {
|
||||
ContentType ContentType
|
||||
Desc string
|
||||
@@ -22,25 +27,68 @@ type File struct {
|
||||
Writer func(w io.Writer) (int64, error)
|
||||
}
|
||||
|
||||
// WithFileName sets the filename of the File
|
||||
// WithFileContentID sets the "Content-ID" header in the File's MIME headers to the specified ID.
|
||||
//
|
||||
// This function updates the File's MIME headers by setting the "Content-ID" to the provided string value,
|
||||
// allowing the file to be referenced by this ID within the MIME structure.
|
||||
//
|
||||
// Parameters:
|
||||
// - id: A string representing the content ID to be set in the "Content-ID" header.
|
||||
//
|
||||
// Returns:
|
||||
// - A FileOption function that updates the File's "Content-ID" header.
|
||||
func WithFileContentID(id string) FileOption {
|
||||
return func(f *File) {
|
||||
f.Header.Set(HeaderContentID.String(), id)
|
||||
}
|
||||
}
|
||||
|
||||
// WithFileName sets the name of a File to the provided value.
|
||||
//
|
||||
// This function assigns the specified name to the File, updating its Name field.
|
||||
//
|
||||
// Parameters:
|
||||
// - name: A string representing the name to be assigned to the File.
|
||||
//
|
||||
// Returns:
|
||||
// - A FileOption function that sets the File's name.
|
||||
func WithFileName(name string) FileOption {
|
||||
return func(f *File) {
|
||||
f.Name = name
|
||||
}
|
||||
}
|
||||
|
||||
// WithFileDescription sets an optional file description of the File that will be
|
||||
// added as Content-Description part
|
||||
// WithFileDescription sets an optional description for the File, which is used in the Content-Description
|
||||
// header of the MIME output.
|
||||
//
|
||||
// This function updates the File's description, allowing an additional text description to be added to
|
||||
// the MIME headers for the file.
|
||||
//
|
||||
// Parameters:
|
||||
// - description: A string representing the description to be set in the Content-Description header.
|
||||
//
|
||||
// Returns:
|
||||
// - A FileOption function that sets the File's description.
|
||||
func WithFileDescription(description string) FileOption {
|
||||
return func(f *File) {
|
||||
f.Desc = description
|
||||
}
|
||||
}
|
||||
|
||||
// WithFileEncoding sets the encoding of the File. By default we should always use
|
||||
// Base64 encoding but there might be exceptions, where this might come handy.
|
||||
// Please note that quoted-printable should never be used for attachments/embeds. If this
|
||||
// is provided as argument, the function will automatically override back to Base64
|
||||
// WithFileEncoding sets the encoding type for a File.
|
||||
//
|
||||
// This function allows the specification of an encoding type for the file, typically used for attachments
|
||||
// or embedded files. By default, Base64 encoding should be used, but this function can override the
|
||||
// default if needed.
|
||||
//
|
||||
// Note: Quoted-printable encoding (EncodingQP) must never be used for attachments or embeds. If EncodingQP
|
||||
// is passed to this function, it will be ignored and the encoding will remain unchanged.
|
||||
//
|
||||
// Parameters:
|
||||
// - encoding: The Encoding type to be assigned to the File, unless it's EncodingQP.
|
||||
//
|
||||
// Returns:
|
||||
// - A FileOption function that sets the File's encoding.
|
||||
func WithFileEncoding(encoding Encoding) FileOption {
|
||||
return func(f *File) {
|
||||
if encoding == EncodingQP {
|
||||
@@ -51,23 +99,45 @@ func WithFileEncoding(encoding Encoding) FileOption {
|
||||
}
|
||||
|
||||
// WithFileContentType sets the content type of the File.
|
||||
// By default go-mail will try to guess the file type and its corresponding
|
||||
// content type and fall back to application/octet-stream if the file type
|
||||
// could not be guessed. In some cases, however, it might be needed to force
|
||||
// this to a specific type. For such situations this override method can
|
||||
// be used
|
||||
//
|
||||
// By default, the content type is guessed based on the file type, and if no matching type is identified,
|
||||
// the default "application/octet-stream" is used. This FileOption allows overriding the guessed content
|
||||
// type with a specific one if required.
|
||||
//
|
||||
// Parameters:
|
||||
// - contentType: The ContentType to be assigned to the File.
|
||||
//
|
||||
// Returns:
|
||||
// - A FileOption function that sets the File's content type.
|
||||
func WithFileContentType(contentType ContentType) FileOption {
|
||||
return func(f *File) {
|
||||
f.ContentType = contentType
|
||||
}
|
||||
}
|
||||
|
||||
// setHeader sets header fields to a File
|
||||
// setHeader sets the value of a specified MIME header field for the File.
|
||||
//
|
||||
// This method updates the MIME headers of the File by assigning the provided value to the specified
|
||||
// header field.
|
||||
//
|
||||
// Parameters:
|
||||
// - header: The Header field to be updated.
|
||||
// - value: A string representing the value to be set for the given header.
|
||||
func (f *File) setHeader(header Header, value string) {
|
||||
f.Header.Set(string(header), value)
|
||||
}
|
||||
|
||||
// getHeader return header fields of a File
|
||||
// getHeader retrieves the value of the specified MIME header field.
|
||||
//
|
||||
// This method returns the value of the given header and a boolean indicating whether the header was found
|
||||
// in the File's MIME headers.
|
||||
//
|
||||
// Parameters:
|
||||
// - header: The Header field whose value is to be retrieved.
|
||||
//
|
||||
// Returns:
|
||||
// - A string containing the value of the header.
|
||||
// - A boolean indicating whether the header was present (true) or not (false).
|
||||
func (f *File) getHeader(header Header) (string, bool) {
|
||||
v := f.Header.Get(string(header))
|
||||
return v, v != ""
|
||||
|
||||
+107
-48
@@ -1,129 +1,151 @@
|
||||
// SPDX-FileCopyrightText: 2022-2023 The go-mail Authors
|
||||
// SPDX-FileCopyrightText: The go-mail Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package mail
|
||||
|
||||
// Header represents a generic mail header field name
|
||||
import "strings"
|
||||
|
||||
// Header is a type wrapper for a string and represents email header fields in a Msg.
|
||||
type Header string
|
||||
|
||||
// AddrHeader represents a address related mail Header field name
|
||||
// AddrHeader is a type wrapper for a string and represents email address headers fields in a Msg.
|
||||
type AddrHeader string
|
||||
|
||||
// Importance represents a Importance/Priority value string
|
||||
// Importance is a type wrapper for an int and represents the level of importance or priority for a Msg.
|
||||
type Importance int
|
||||
|
||||
// List of common generic header field names
|
||||
const (
|
||||
// HeaderContentDescription is the "Content-Description" header
|
||||
// HeaderContentDescription is the "Content-Description" header.
|
||||
HeaderContentDescription Header = "Content-Description"
|
||||
|
||||
// HeaderContentDisposition is the "Content-Disposition" header
|
||||
// HeaderContentDisposition is the "Content-Disposition" header.
|
||||
HeaderContentDisposition Header = "Content-Disposition"
|
||||
|
||||
// HeaderContentID is the "Content-ID" header
|
||||
// HeaderContentID is the "Content-ID" header.
|
||||
HeaderContentID Header = "Content-ID"
|
||||
|
||||
// HeaderContentLang is the "Content-Language" header
|
||||
// HeaderContentLang is the "Content-Language" header.
|
||||
HeaderContentLang Header = "Content-Language"
|
||||
|
||||
// HeaderContentLocation is the "Content-Location" header (RFC 2110)
|
||||
// HeaderContentLocation is the "Content-Location" header (RFC 2110).
|
||||
// https://datatracker.ietf.org/doc/html/rfc2110#section-4.3
|
||||
HeaderContentLocation Header = "Content-Location"
|
||||
|
||||
// HeaderContentTransferEnc is the "Content-Transfer-Encoding" header
|
||||
// HeaderContentTransferEnc is the "Content-Transfer-Encoding" header.
|
||||
HeaderContentTransferEnc Header = "Content-Transfer-Encoding"
|
||||
|
||||
// HeaderContentType is the "Content-Type" header
|
||||
// HeaderContentType is the "Content-Type" header.
|
||||
HeaderContentType Header = "Content-Type"
|
||||
|
||||
// HeaderDate represents the "Date" field
|
||||
// See: https://www.rfc-editor.org/rfc/rfc822#section-5.1
|
||||
// HeaderDate represents the "Date" field.
|
||||
// https://datatracker.ietf.org/doc/html/rfc822#section-5.1
|
||||
HeaderDate Header = "Date"
|
||||
|
||||
// HeaderDispositionNotificationTo is the MDN header as described in RFC8098
|
||||
// See: https://www.rfc-editor.org/rfc/rfc8098.html#section-2.1
|
||||
// HeaderDispositionNotificationTo is the MDN header as described in RFC 8098.
|
||||
// https://datatracker.ietf.org/doc/html/rfc8098#section-2.1
|
||||
HeaderDispositionNotificationTo Header = "Disposition-Notification-To"
|
||||
|
||||
// HeaderImportance represents the "Importance" field
|
||||
// HeaderImportance represents the "Importance" field.
|
||||
HeaderImportance Header = "Importance"
|
||||
|
||||
// HeaderInReplyTo represents the "In-Reply-To" field
|
||||
// HeaderInReplyTo represents the "In-Reply-To" field.
|
||||
HeaderInReplyTo Header = "In-Reply-To"
|
||||
|
||||
// HeaderListUnsubscribe is the "List-Unsubscribe" header field
|
||||
// HeaderListUnsubscribe is the "List-Unsubscribe" header field.
|
||||
HeaderListUnsubscribe Header = "List-Unsubscribe"
|
||||
|
||||
// HeaderListUnsubscribePost is the "List-Unsubscribe-Post" header field
|
||||
// HeaderListUnsubscribePost is the "List-Unsubscribe-Post" header field.
|
||||
HeaderListUnsubscribePost Header = "List-Unsubscribe-Post"
|
||||
|
||||
// HeaderMessageID represents the "Message-ID" field for message identification
|
||||
// See: https://www.rfc-editor.org/rfc/rfc1036#section-2.1.5
|
||||
// HeaderMessageID represents the "Message-ID" field for message identification.
|
||||
// https://datatracker.ietf.org/doc/html/rfc1036#section-2.1.5
|
||||
HeaderMessageID Header = "Message-ID"
|
||||
|
||||
// HeaderMIMEVersion represents the "MIME-Version" field as per RFC 2045
|
||||
// See: https://datatracker.ietf.org/doc/html/rfc2045#section-4
|
||||
// HeaderMIMEVersion represents the "MIME-Version" field as per RFC 2045.
|
||||
// https://datatracker.ietf.org/doc/html/rfc2045#section-4
|
||||
HeaderMIMEVersion Header = "MIME-Version"
|
||||
|
||||
// HeaderOrganization is the "Organization" header field
|
||||
// HeaderOrganization is the "Organization" header field.
|
||||
HeaderOrganization Header = "Organization"
|
||||
|
||||
// HeaderPrecedence is the "Precedence" header field
|
||||
// HeaderPrecedence is the "Precedence" header field.
|
||||
HeaderPrecedence Header = "Precedence"
|
||||
|
||||
// HeaderPriority represents the "Priority" field
|
||||
// HeaderPriority represents the "Priority" field.
|
||||
HeaderPriority Header = "Priority"
|
||||
|
||||
// HeaderReplyTo is the "Reply-To" header field
|
||||
HeaderReplyTo Header = "Reply-To"
|
||||
// HeaderReferences is the "References" header field.
|
||||
HeaderReferences Header = "References"
|
||||
|
||||
// HeaderSubject is the "Subject" header field
|
||||
// HeaderSubject is the "Subject" header field.
|
||||
HeaderSubject Header = "Subject"
|
||||
|
||||
// HeaderUserAgent is the "User-Agent" header field
|
||||
// HeaderUserAgent is the "User-Agent" header field.
|
||||
HeaderUserAgent Header = "User-Agent"
|
||||
|
||||
// HeaderXAutoResponseSuppress is the "X-Auto-Response-Suppress" header field
|
||||
// HeaderXAutoResponseSuppress is the "X-Auto-Response-Suppress" header field.
|
||||
HeaderXAutoResponseSuppress Header = "X-Auto-Response-Suppress"
|
||||
|
||||
// HeaderXMailer is the "X-Mailer" header field
|
||||
// HeaderXMailer is the "X-Mailer" header field.
|
||||
HeaderXMailer Header = "X-Mailer"
|
||||
|
||||
// HeaderXMSMailPriority is the "X-MSMail-Priority" header field
|
||||
// HeaderXMSMailPriority is the "X-MSMail-Priority" header field.
|
||||
HeaderXMSMailPriority Header = "X-MSMail-Priority"
|
||||
|
||||
// HeaderXPriority is the "X-Priority" header field
|
||||
// HeaderXPriority is the "X-Priority" header field.
|
||||
HeaderXPriority Header = "X-Priority"
|
||||
)
|
||||
|
||||
// List of common address header field names
|
||||
const (
|
||||
// HeaderBcc is the "Blind Carbon Copy" header field
|
||||
// HeaderBcc is the "Blind Carbon Copy" header field.
|
||||
HeaderBcc AddrHeader = "Bcc"
|
||||
|
||||
// HeaderCc is the "Carbon Copy" header field
|
||||
// HeaderCc is the "Carbon Copy" header field.
|
||||
HeaderCc AddrHeader = "Cc"
|
||||
|
||||
// HeaderEnvelopeFrom is the envelope FROM header field
|
||||
// It's not included in the mail body but only used by the Client for the envelope
|
||||
// HeaderEnvelopeFrom is the envelope FROM header field.
|
||||
//
|
||||
// It is generally not included in the mail body but only used by the Client for the communication with the
|
||||
// SMTP server. If the Msg has no "FROM" address set in the mail body, the msgWriter will try to use the
|
||||
// envelope from address, if this has been set for the Msg.
|
||||
HeaderEnvelopeFrom AddrHeader = "EnvelopeFrom"
|
||||
|
||||
// HeaderFrom is the "From" header field
|
||||
// HeaderFrom is the "From" header field.
|
||||
HeaderFrom AddrHeader = "From"
|
||||
|
||||
// HeaderTo is the "Receipient" header field
|
||||
// HeaderReplyTo is the "Reply-To" header field.
|
||||
HeaderReplyTo AddrHeader = "Reply-To"
|
||||
|
||||
// HeaderTo is the "Recipient" header field.
|
||||
HeaderTo AddrHeader = "To"
|
||||
)
|
||||
|
||||
// List of Importance values
|
||||
const (
|
||||
// ImportanceLow indicates a low level of importance or priority in a Msg.
|
||||
ImportanceLow Importance = iota
|
||||
|
||||
// ImportanceNormal indicates a standard level of importance or priority for a Msg.
|
||||
ImportanceNormal
|
||||
|
||||
// ImportanceHigh indicates a high level of importance or priority in a Msg.
|
||||
ImportanceHigh
|
||||
|
||||
// ImportanceNonUrgent indicates a non-urgent level of importance or priority in a Msg.
|
||||
ImportanceNonUrgent
|
||||
|
||||
// ImportanceUrgent indicates an urgent level of importance or priority in a Msg.
|
||||
ImportanceUrgent
|
||||
)
|
||||
|
||||
// NumString returns the importance number string based on the Importance
|
||||
// NumString returns a numerical string representation of the Importance level.
|
||||
//
|
||||
// This method maps ImportanceHigh and ImportanceUrgent to "1", while ImportanceNonUrgent and ImportanceLow
|
||||
// are mapped to "0". Other values return an empty string.
|
||||
//
|
||||
// Returns:
|
||||
// - A string representing the numerical value of the Importance level ("1" or "0"), or an empty string
|
||||
// if the Importance level is unrecognized.
|
||||
func (i Importance) NumString() string {
|
||||
switch i {
|
||||
case ImportanceNonUrgent:
|
||||
@@ -139,7 +161,14 @@ func (i Importance) NumString() string {
|
||||
}
|
||||
}
|
||||
|
||||
// XPrioString returns the X-Priority number string based on the Importance
|
||||
// XPrioString returns the X-Priority string representation of the Importance level.
|
||||
//
|
||||
// This method maps ImportanceHigh and ImportanceUrgent to "1", while ImportanceNonUrgent and ImportanceLow
|
||||
// are mapped to "5". Other values return an empty string.
|
||||
//
|
||||
// Returns:
|
||||
// - A string representing the X-Priority value of the Importance level ("1" or "5"), or an empty string
|
||||
// if the Importance level is unrecognized.
|
||||
func (i Importance) XPrioString() string {
|
||||
switch i {
|
||||
case ImportanceNonUrgent:
|
||||
@@ -155,7 +184,14 @@ func (i Importance) XPrioString() string {
|
||||
}
|
||||
}
|
||||
|
||||
// String returns the importance string based on the Importance
|
||||
// String satisfies the fmt.Stringer interface for the Importance type and returns the string
|
||||
// representation of the Importance level.
|
||||
//
|
||||
// This method provides a human-readable string for each Importance level.
|
||||
//
|
||||
// Returns:
|
||||
// - A string representing the Importance level ("non-urgent", "low", "high", or "urgent"), or an empty
|
||||
// string if the Importance level is unrecognized.
|
||||
func (i Importance) String() string {
|
||||
switch i {
|
||||
case ImportanceNonUrgent:
|
||||
@@ -171,12 +207,35 @@ func (i Importance) String() string {
|
||||
}
|
||||
}
|
||||
|
||||
// String returns the header string based on the given Header
|
||||
// String satisfies the fmt.Stringer interface for the Header type and returns the string
|
||||
// representation of the Header.
|
||||
//
|
||||
// Returns:
|
||||
// - A string representing the Header.
|
||||
func (h Header) String() string {
|
||||
return string(h)
|
||||
}
|
||||
|
||||
// String returns the address header string based on the given AddrHeader
|
||||
// String satisfies the fmt.Stringer interface for the AddrHeader type and returns the string
|
||||
// representation of the AddrHeader.
|
||||
//
|
||||
// Returns:
|
||||
// - A string representing the AddrHeader.
|
||||
func (a AddrHeader) String() string {
|
||||
return string(a)
|
||||
}
|
||||
|
||||
// IsAddrHeader checks if the provided string is an address header. It returns true on a valid AddrHeader
|
||||
// and false for any other string.
|
||||
//
|
||||
// Parameters:
|
||||
// - header: The string to check.
|
||||
func IsAddrHeader(header string) bool {
|
||||
addrHeaderList := []string{"bcc", "cc", "envelopefrom", "from", "reply-to", "to"}
|
||||
for _, addrHeader := range addrHeaderList {
|
||||
if addrHeader == strings.ToLower(header) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
+23
-20
@@ -1,10 +1,7 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2023 The go-mail Authors
|
||||
// SPDX-FileCopyrightText: Copyright (c) The go-mail Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//go:build go1.21
|
||||
// +build go1.21
|
||||
|
||||
package log
|
||||
|
||||
import (
|
||||
@@ -41,42 +38,48 @@ func NewJSON(output io.Writer, level Level) *JSONlog {
|
||||
}
|
||||
}
|
||||
|
||||
// logMessage is a helper function to handle different log levels and formats.
|
||||
func logMessage(level Level, log *slog.Logger, logData Log, formatFunc func(string, ...interface{}) string) {
|
||||
lGroup := log.WithGroup(DirString).With(
|
||||
slog.String(DirFromString, logData.directionFrom()),
|
||||
slog.String(DirToString, logData.directionTo()),
|
||||
)
|
||||
switch level {
|
||||
case LevelDebug:
|
||||
lGroup.Debug(formatFunc(logData.Format, logData.Messages...))
|
||||
case LevelInfo:
|
||||
lGroup.Info(formatFunc(logData.Format, logData.Messages...))
|
||||
case LevelWarn:
|
||||
lGroup.Warn(formatFunc(logData.Format, logData.Messages...))
|
||||
case LevelError:
|
||||
lGroup.Error(formatFunc(logData.Format, logData.Messages...))
|
||||
}
|
||||
}
|
||||
|
||||
// Debugf logs a debug message via the structured JSON logger
|
||||
func (l *JSONlog) Debugf(log Log) {
|
||||
if l.level >= LevelDebug {
|
||||
l.log.WithGroup(DirString).With(
|
||||
slog.String(DirFromString, log.directionFrom()),
|
||||
slog.String(DirToString, log.directionTo()),
|
||||
).Debug(fmt.Sprintf(log.Format, log.Messages...))
|
||||
logMessage(LevelDebug, l.log, log, fmt.Sprintf)
|
||||
}
|
||||
}
|
||||
|
||||
// Infof logs a info message via the structured JSON logger
|
||||
func (l *JSONlog) Infof(log Log) {
|
||||
if l.level >= LevelInfo {
|
||||
l.log.WithGroup(DirString).With(
|
||||
slog.String(DirFromString, log.directionFrom()),
|
||||
slog.String(DirToString, log.directionTo()),
|
||||
).Info(fmt.Sprintf(log.Format, log.Messages...))
|
||||
logMessage(LevelInfo, l.log, log, fmt.Sprintf)
|
||||
}
|
||||
}
|
||||
|
||||
// Warnf logs a warn message via the structured JSON logger
|
||||
func (l *JSONlog) Warnf(log Log) {
|
||||
if l.level >= LevelWarn {
|
||||
l.log.WithGroup(DirString).With(
|
||||
slog.String(DirFromString, log.directionFrom()),
|
||||
slog.String(DirToString, log.directionTo()),
|
||||
).Warn(fmt.Sprintf(log.Format, log.Messages...))
|
||||
logMessage(LevelWarn, l.log, log, fmt.Sprintf)
|
||||
}
|
||||
}
|
||||
|
||||
// Errorf logs a warn message via the structured JSON logger
|
||||
func (l *JSONlog) Errorf(log Log) {
|
||||
if l.level >= LevelError {
|
||||
l.log.WithGroup(DirString).With(
|
||||
slog.String(DirFromString, log.directionFrom()),
|
||||
slog.String(DirToString, log.directionTo()),
|
||||
).Error(fmt.Sprintf(log.Format, log.Messages...))
|
||||
logMessage(LevelError, l.log, log, fmt.Sprintf)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2022-2023 The go-mail Authors
|
||||
// SPDX-FileCopyrightText: Copyright (c) The go-mail Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
|
||||
+11
-9
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2023 The go-mail Authors
|
||||
// SPDX-FileCopyrightText: Copyright (c) The go-mail Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
@@ -35,34 +35,36 @@ func New(output io.Writer, level Level) *Stdlog {
|
||||
}
|
||||
}
|
||||
|
||||
// logStdMessage is a helper function to handle different log levels and formats for Stdlog.
|
||||
func logStdMessage(logger *log.Logger, logData Log, callDepth int) {
|
||||
format := fmt.Sprintf("%s %s", logData.directionPrefix(), logData.Format)
|
||||
_ = logger.Output(callDepth, fmt.Sprintf(format, logData.Messages...))
|
||||
}
|
||||
|
||||
// Debugf performs a Printf() on the debug logger
|
||||
func (l *Stdlog) Debugf(log Log) {
|
||||
if l.level >= LevelDebug {
|
||||
format := fmt.Sprintf("%s %s", log.directionPrefix(), log.Format)
|
||||
_ = l.debug.Output(CallDepth, fmt.Sprintf(format, log.Messages...))
|
||||
logStdMessage(l.debug, log, CallDepth)
|
||||
}
|
||||
}
|
||||
|
||||
// Infof performs a Printf() on the info logger
|
||||
func (l *Stdlog) Infof(log Log) {
|
||||
if l.level >= LevelInfo {
|
||||
format := fmt.Sprintf("%s %s", log.directionPrefix(), log.Format)
|
||||
_ = l.info.Output(CallDepth, fmt.Sprintf(format, log.Messages...))
|
||||
logStdMessage(l.info, log, CallDepth)
|
||||
}
|
||||
}
|
||||
|
||||
// Warnf performs a Printf() on the warn logger
|
||||
func (l *Stdlog) Warnf(log Log) {
|
||||
if l.level >= LevelWarn {
|
||||
format := fmt.Sprintf("%s %s", log.directionPrefix(), log.Format)
|
||||
_ = l.warn.Output(CallDepth, fmt.Sprintf(format, log.Messages...))
|
||||
logStdMessage(l.warn, log, CallDepth)
|
||||
}
|
||||
}
|
||||
|
||||
// Errorf performs a Printf() on the error logger
|
||||
func (l *Stdlog) Errorf(log Log) {
|
||||
if l.level >= LevelError {
|
||||
format := fmt.Sprintf("%s %s", log.directionPrefix(), log.Format)
|
||||
_ = l.err.Output(CallDepth, fmt.Sprintf(format, log.Messages...))
|
||||
logStdMessage(l.err, log, CallDepth)
|
||||
}
|
||||
}
|
||||
|
||||
+2334
-351
File diff suppressed because it is too large
Load Diff
+9
-6
@@ -1,10 +1,7 @@
|
||||
// SPDX-FileCopyrightText: 2022-2023 The go-mail Authors
|
||||
// SPDX-FileCopyrightText: The go-mail Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//go:build go1.17
|
||||
// +build go1.17
|
||||
|
||||
package mail
|
||||
|
||||
import (
|
||||
@@ -12,8 +9,14 @@ import (
|
||||
"os"
|
||||
)
|
||||
|
||||
// WriteToTempFile will create a temporary file and output the Msg to this file
|
||||
// The method will return the filename of the temporary file
|
||||
// WriteToTempFile creates a temporary file and writes the Msg content to this file.
|
||||
//
|
||||
// This method generates a temporary file with a ".eml" extension, writes the Msg to it, and returns the
|
||||
// filename of the created temporary file.
|
||||
//
|
||||
// Returns:
|
||||
// - A string representing the filename of the temporary file.
|
||||
// - An error if the file creation or writing process fails.
|
||||
func (m *Msg) WriteToTempFile() (string, error) {
|
||||
f, err := os.CreateTemp("", "go-mail_*.eml")
|
||||
if err != nil {
|
||||
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2022-2023 The go-mail Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//go:build !go1.17
|
||||
// +build !go1.17
|
||||
|
||||
package mail
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
// WriteToTempFile will create a temporary file and output the Msg to this file
|
||||
// The method will return the filename of the temporary file
|
||||
func (m *Msg) WriteToTempFile() (string, error) {
|
||||
f, err := ioutil.TempFile("", "go-mail_*.eml")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create output file: %w", err)
|
||||
}
|
||||
defer func() { _ = f.Close() }()
|
||||
return f.Name(), m.WriteToFile(f.Name())
|
||||
}
|
||||
+281
-55
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: 2022-2023 The go-mail Authors
|
||||
// SPDX-FileCopyrightText: The go-mail Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
@@ -18,34 +18,62 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// MaxHeaderLength defines the maximum line length for a mail header
|
||||
// RFC 2047 suggests 76 characters
|
||||
const MaxHeaderLength = 76
|
||||
const (
|
||||
// MaxHeaderLength defines the maximum line length for a mail header.
|
||||
//
|
||||
// This constant follows the recommendation of RFC 2047, which suggests a maximum length of 76 characters.
|
||||
//
|
||||
// References:
|
||||
// - https://datatracker.ietf.org/doc/html/rfc2047
|
||||
MaxHeaderLength = 76
|
||||
|
||||
// MaxBodyLength defines the maximum line length for the mail body
|
||||
// RFC 2047 suggests 76 characters
|
||||
const MaxBodyLength = 76
|
||||
// MaxBodyLength defines the maximum line length for the mail body.
|
||||
//
|
||||
// This constant follows the recommendation of RFC 2047, which suggests a maximum length of 76 characters.
|
||||
//
|
||||
// References:
|
||||
// - https://datatracker.ietf.org/doc/html/rfc2047
|
||||
MaxBodyLength = 76
|
||||
|
||||
// SingleNewLine represents a new line that can be used by the msgWriter to issue a carriage return
|
||||
const SingleNewLine = "\r\n"
|
||||
// SingleNewLine represents a single newline character sequence ("\r\n").
|
||||
//
|
||||
// This constant can be used by the msgWriter to issue a carriage return when writing mail content.
|
||||
SingleNewLine = "\r\n"
|
||||
|
||||
// DoubleNewLine represents a double new line that can be used by the msgWriter to
|
||||
// indicate a new segement of the mail
|
||||
const DoubleNewLine = "\r\n\r\n"
|
||||
// DoubleNewLine represents a double newline character sequence ("\r\n\r\n").
|
||||
//
|
||||
// This constant can be used by the msgWriter to indicate a new segment of the mail when writing mail content.
|
||||
DoubleNewLine = "\r\n\r\n"
|
||||
)
|
||||
|
||||
// msgWriter handles the I/O to the io.WriteCloser of the SMTP client
|
||||
// msgWriter handles the I/O operations for writing to the io.WriteCloser of the SMTP client.
|
||||
//
|
||||
// This struct keeps track of the number of bytes written, the character set used, and the depth of the
|
||||
// current multipart section. It also handles encoding, error tracking, and managing multipart and part
|
||||
// writers for constructing the email message body.
|
||||
type msgWriter struct {
|
||||
bytesWritten int64
|
||||
charset Charset
|
||||
depth int8
|
||||
encoder mime.WordEncoder
|
||||
err error
|
||||
multiPartWriter [3]*multipart.Writer
|
||||
multiPartWriter [4]*multipart.Writer
|
||||
partWriter io.Writer
|
||||
writer io.Writer
|
||||
}
|
||||
|
||||
// Write implements the io.Writer interface for msgWriter
|
||||
// Write implements the io.Writer interface for msgWriter.
|
||||
//
|
||||
// This method writes the provided payload to the underlying writer. It keeps track of the number of bytes
|
||||
// written and handles any errors encountered during the writing process. If a previous error exists, it
|
||||
// prevents further writing and returns the error.
|
||||
//
|
||||
// Parameters:
|
||||
// - payload: A byte slice containing the data to be written.
|
||||
//
|
||||
// Returns:
|
||||
// - The number of bytes successfully written.
|
||||
// - An error if the writing process fails, or if a previous error was encountered.
|
||||
func (mw *msgWriter) Write(payload []byte) (int, error) {
|
||||
if mw.err != nil {
|
||||
return 0, fmt.Errorf("failed to write due to previous error: %w", mw.err)
|
||||
@@ -57,7 +85,19 @@ func (mw *msgWriter) Write(payload []byte) (int, error) {
|
||||
return n, mw.err
|
||||
}
|
||||
|
||||
// writeMsg formats the message and sends it to its io.Writer
|
||||
// writeMsg formats the message and writes it to the msgWriter's io.Writer.
|
||||
//
|
||||
// This method handles the process of writing the message headers and body content, including handling
|
||||
// multipart structures (e.g., mixed, related, alternative), PGP types, and attachments/embeds. It sets the
|
||||
// required headers (e.g., "From", "To", "Cc") and iterates over the message parts, writing them to the
|
||||
// output writer.
|
||||
//
|
||||
// Parameters:
|
||||
// - msg: A pointer to the Msg struct containing the message data and headers to be written.
|
||||
//
|
||||
// References:
|
||||
// - https://datatracker.ietf.org/doc/html/rfc2045 (Multipurpose Internet Mail Extensions - MIME)
|
||||
// - https://datatracker.ietf.org/doc/html/rfc5322 (Internet Message Format)
|
||||
func (mw *msgWriter) writeMsg(msg *Msg) {
|
||||
msg.addDefaultHeader()
|
||||
msg.checkUserAgent()
|
||||
@@ -74,39 +114,63 @@ func (mw *msgWriter) writeMsg(msg *Msg) {
|
||||
}
|
||||
}
|
||||
if hasFrom && (len(from) > 0 && from[0] != nil) {
|
||||
mw.writeHeader(Header(HeaderFrom), from[0].String())
|
||||
msg.headerCount += mw.writeHeader(Header(HeaderFrom), from[0].String())
|
||||
}
|
||||
|
||||
// Set the rest of the address headers
|
||||
for _, to := range []AddrHeader{HeaderTo, HeaderCc} {
|
||||
for _, to := range []AddrHeader{HeaderTo, HeaderCc, HeaderReplyTo} {
|
||||
if addresses, ok := msg.addrHeader[to]; ok {
|
||||
var val []string
|
||||
for _, addr := range addresses {
|
||||
if addr == nil {
|
||||
continue
|
||||
}
|
||||
val = append(val, addr.String())
|
||||
}
|
||||
mw.writeHeader(Header(to), val...)
|
||||
msg.headerCount += mw.writeHeader(Header(to), val...)
|
||||
}
|
||||
}
|
||||
|
||||
if msg.hasMixed() {
|
||||
mw.startMP("mixed", msg.boundary)
|
||||
if msg.hasSMIME() && !msg.isSMIMEInProgress() {
|
||||
boundary, err := randomBoundary()
|
||||
if err != nil {
|
||||
mw.err = err
|
||||
return
|
||||
}
|
||||
mw.startMP(msg, MIMESMIMESigned, boundary)
|
||||
mw.writeString(DoubleNewLine)
|
||||
}
|
||||
if msg.hasMixed() {
|
||||
boundary := mw.getMultipartBoundary(msg, MIMEMixed)
|
||||
boundary = mw.startMP(msg, MIMEMixed, boundary)
|
||||
msg.multiPartBoundary[MIMEMixed] = boundary
|
||||
if mw.depth == 1 {
|
||||
mw.writeString(DoubleNewLine)
|
||||
}
|
||||
}
|
||||
if msg.hasRelated() {
|
||||
mw.startMP("related", msg.boundary)
|
||||
mw.writeString(DoubleNewLine)
|
||||
boundary := mw.getMultipartBoundary(msg, MIMERelated)
|
||||
boundary = mw.startMP(msg, MIMERelated, boundary)
|
||||
msg.multiPartBoundary[MIMERelated] = boundary
|
||||
if mw.depth == 1 {
|
||||
mw.writeString(DoubleNewLine)
|
||||
}
|
||||
}
|
||||
if msg.hasAlt() {
|
||||
mw.startMP(MIMEAlternative, msg.boundary)
|
||||
mw.writeString(DoubleNewLine)
|
||||
boundary := mw.getMultipartBoundary(msg, MIMEAlternative)
|
||||
boundary = mw.startMP(msg, MIMEAlternative, boundary)
|
||||
msg.multiPartBoundary[MIMEAlternative] = boundary
|
||||
if mw.depth == 1 {
|
||||
mw.writeString(DoubleNewLine)
|
||||
}
|
||||
}
|
||||
if msg.hasPGPType() {
|
||||
switch msg.pgptype {
|
||||
case PGPEncrypt:
|
||||
mw.startMP(`encrypted; protocol="application/pgp-encrypted"`,
|
||||
mw.startMP(msg, `encrypted; protocol="application/pgp-encrypted"`,
|
||||
msg.boundary)
|
||||
case PGPSignature:
|
||||
mw.startMP(`signed; protocol="application/pgp-signature";`,
|
||||
mw.startMP(msg, `signed; protocol="application/pgp-signature";`,
|
||||
msg.boundary)
|
||||
default:
|
||||
}
|
||||
@@ -114,7 +178,7 @@ func (mw *msgWriter) writeMsg(msg *Msg) {
|
||||
}
|
||||
|
||||
for _, part := range msg.parts {
|
||||
if !part.isDeleted {
|
||||
if !part.isDeleted && !part.smime {
|
||||
mw.writePart(part, msg.charset)
|
||||
}
|
||||
}
|
||||
@@ -134,29 +198,69 @@ func (mw *msgWriter) writeMsg(msg *Msg) {
|
||||
if msg.hasMixed() {
|
||||
mw.stopMP()
|
||||
}
|
||||
|
||||
if msg.hasSMIME() && !msg.isSMIMEInProgress() {
|
||||
for _, part := range msg.parts {
|
||||
if part.smime {
|
||||
mw.writePart(part, msg.charset)
|
||||
}
|
||||
}
|
||||
mw.stopMP()
|
||||
}
|
||||
}
|
||||
|
||||
// writeGenHeader writes out all generic headers to the msgWriter
|
||||
// writeGenHeader writes out all generic headers to the msgWriter.
|
||||
//
|
||||
// This function extracts all generic headers from the provided Msg object, sorts them, and writes them
|
||||
// to the msgWriter in alphabetical order.
|
||||
//
|
||||
// Parameters:
|
||||
// - msg: The Msg object containing the headers to be written.
|
||||
func (mw *msgWriter) writeGenHeader(msg *Msg) {
|
||||
keys := make([]string, 0, len(msg.genHeader))
|
||||
for key := range msg.genHeader {
|
||||
keys = append(keys, string(key))
|
||||
}
|
||||
|
||||
sort.Strings(keys)
|
||||
for _, key := range keys {
|
||||
mw.writeHeader(Header(key), msg.genHeader[Header(key)]...)
|
||||
msg.headerCount += mw.writeHeader(Header(key), msg.genHeader[Header(key)]...)
|
||||
}
|
||||
}
|
||||
|
||||
// writePreformatedHeader writes out all preformated generic headers to the msgWriter
|
||||
// writePreformattedGenHeader writes out all preformatted generic headers to the msgWriter.
|
||||
//
|
||||
// This function iterates over all preformatted generic headers from the provided Msg object and writes
|
||||
// them to the msgWriter in the format "key: value" followed by a newline.
|
||||
//
|
||||
// Parameters:
|
||||
// - msg: The Msg object containing the preformatted headers to be written.
|
||||
func (mw *msgWriter) writePreformattedGenHeader(msg *Msg) {
|
||||
for key, val := range msg.preformHeader {
|
||||
mw.writeString(fmt.Sprintf("%s: %s%s", key, val, SingleNewLine))
|
||||
line := fmt.Sprintf("%s: %s%s", key, val, SingleNewLine)
|
||||
mw.writeString(line)
|
||||
msg.headerCount += strings.Count(line, SingleNewLine)
|
||||
}
|
||||
}
|
||||
|
||||
// startMP writes a multipart beginning
|
||||
func (mw *msgWriter) startMP(mimeType MIMEType, boundary string) {
|
||||
// startMP writes a multipart beginning.
|
||||
//
|
||||
// This function initializes a multipart writer for the msgWriter using the specified MIME type and
|
||||
// boundary. It sets the Content-Type header to indicate the multipart type and writes the boundary
|
||||
// information. If a boundary is provided, it is set explicitly; otherwise, a default boundary is
|
||||
// generated. It also handles writing a new part when nested multipart structures are used.
|
||||
//
|
||||
// Parameters:
|
||||
// - msg: The message whose multipart headers are being written.
|
||||
// - mimeType: The MIME type of the multipart content (e.g., "mixed", "alternative").
|
||||
// - boundary: The boundary string separating different parts of the multipart message.
|
||||
//
|
||||
// Returns:
|
||||
// - The multipart boundary string
|
||||
//
|
||||
// References:
|
||||
// - https://datatracker.ietf.org/doc/html/rfc2046
|
||||
func (mw *msgWriter) startMP(msg *Msg, mimeType MIMEType, boundary string) string {
|
||||
multiPartWriter := multipart.NewWriter(mw)
|
||||
if boundary != "" {
|
||||
mw.err = multiPartWriter.SetBoundary(boundary)
|
||||
@@ -166,16 +270,24 @@ func (mw *msgWriter) startMP(mimeType MIMEType, boundary string) {
|
||||
multiPartWriter.Boundary())
|
||||
mw.multiPartWriter[mw.depth] = multiPartWriter
|
||||
|
||||
if mw.depth == 0 {
|
||||
mw.writeString(fmt.Sprintf("%s: %s", HeaderContentType, contentType))
|
||||
}
|
||||
if mw.depth > 0 {
|
||||
mw.newPart(map[string][]string{"Content-Type": {contentType}})
|
||||
// Do not write Content-Type if the header was already written as part of genHeaders.
|
||||
if _, ok := msg.genHeader[HeaderContentType]; !ok {
|
||||
if mw.depth == 0 {
|
||||
mw.writeString(fmt.Sprintf("%s: %s", HeaderContentType, contentType))
|
||||
}
|
||||
if mw.depth > 0 {
|
||||
mw.newPart(map[string][]string{"Content-Type": {contentType}})
|
||||
}
|
||||
}
|
||||
|
||||
mw.depth++
|
||||
return multiPartWriter.Boundary()
|
||||
}
|
||||
|
||||
// stopMP closes the multipart
|
||||
// stopMP closes the multipart.
|
||||
//
|
||||
// This function closes the current multipart writer if there is an active multipart structure.
|
||||
// It decreases the depth level of multipart nesting.
|
||||
func (mw *msgWriter) stopMP() {
|
||||
if mw.depth > 0 {
|
||||
mw.err = mw.multiPartWriter[mw.depth-1].Close()
|
||||
@@ -183,7 +295,39 @@ func (mw *msgWriter) stopMP() {
|
||||
}
|
||||
}
|
||||
|
||||
// addFiles adds the attachments/embeds file content to the mail body
|
||||
// getMultipartBoundary returns the appropriate multipart boundary for the given MIME type.
|
||||
//
|
||||
// If the Msg has a predefined boundary, it is returned. Otherwise, the function checks
|
||||
// for a MIME type-specific boundary in the Msg's multiPartBoundary map. If no boundary
|
||||
// is found, an empty string is returned.
|
||||
//
|
||||
// Parameters:
|
||||
// - msg: A pointer to the Msg containing the boundary and MIME type-specific mappings.
|
||||
// - mimetype: The MIMEType for which the boundary is being determined.
|
||||
//
|
||||
// Returns:
|
||||
// - A string representing the multipart boundary, or an empty string if none is found.
|
||||
func (mw *msgWriter) getMultipartBoundary(msg *Msg, mimetype MIMEType) string {
|
||||
if msg.boundary != "" {
|
||||
return msg.boundary
|
||||
}
|
||||
if msg.multiPartBoundary[mimetype] != "" {
|
||||
return msg.multiPartBoundary[mimetype]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// addFiles adds the attachments/embeds file content to the mail body.
|
||||
//
|
||||
// This function iterates through the list of files, setting necessary headers for each file,
|
||||
// including Content-Type, Content-Transfer-Encoding, Content-Disposition, and Content-ID
|
||||
// (if the file is an embed). It determines the appropriate MIME type for each file based on
|
||||
// its extension or the provided ContentType. It writes file headers and file content
|
||||
// to the mail body using the appropriate encoding.
|
||||
//
|
||||
// Parameters:
|
||||
// - files: A slice of File objects to be added to the mail body.
|
||||
// - isAttachment: A boolean indicating whether the files are attachments (true) or embeds (false).
|
||||
func (mw *msgWriter) addFiles(files []*File, isAttachment bool) {
|
||||
for _, file := range files {
|
||||
encoding := EncodingB64
|
||||
@@ -196,7 +340,7 @@ func (mw *msgWriter) addFiles(files []*File, isAttachment bool) {
|
||||
mimeType = string(file.ContentType)
|
||||
}
|
||||
file.setHeader(HeaderContentType, fmt.Sprintf(`%s; name="%s"`, mimeType,
|
||||
mw.encoder.Encode(mw.charset.String(), file.Name)))
|
||||
mw.encoder.Encode(mw.charset.String(), sanitizeFilename(file.Name))))
|
||||
}
|
||||
|
||||
if _, ok := file.getHeader(HeaderContentTransferEnc); !ok {
|
||||
@@ -208,7 +352,7 @@ func (mw *msgWriter) addFiles(files []*File, isAttachment bool) {
|
||||
|
||||
if file.Desc != "" {
|
||||
if _, ok := file.getHeader(HeaderContentDescription); !ok {
|
||||
file.setHeader(HeaderContentDescription, file.Desc)
|
||||
file.setHeader(HeaderContentDescription, mw.encoder.Encode(mw.charset.String(), file.Desc))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,12 +362,12 @@ func (mw *msgWriter) addFiles(files []*File, isAttachment bool) {
|
||||
disposition = "attachment"
|
||||
}
|
||||
file.setHeader(HeaderContentDisposition, fmt.Sprintf(`%s; filename="%s"`,
|
||||
disposition, mw.encoder.Encode(mw.charset.String(), file.Name)))
|
||||
disposition, mw.encoder.Encode(mw.charset.String(), sanitizeFilename(file.Name))))
|
||||
}
|
||||
|
||||
if !isAttachment {
|
||||
if _, ok := file.getHeader(HeaderContentID); !ok {
|
||||
file.setHeader(HeaderContentID, fmt.Sprintf("<%s>", file.Name))
|
||||
file.setHeader(HeaderContentID, fmt.Sprintf("<%s>", sanitizeFilename(file.Name)))
|
||||
}
|
||||
}
|
||||
if mw.depth == 0 {
|
||||
@@ -242,22 +386,44 @@ func (mw *msgWriter) addFiles(files []*File, isAttachment bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// newPart creates a new MIME multipart io.Writer and sets the partwriter to it
|
||||
// newPart creates a new MIME multipart io.Writer and sets the partWriter to it.
|
||||
//
|
||||
// This function creates a new MIME part using the provided header information and assigns it
|
||||
// to the partWriter. It interacts with the current multipart writer at the specified depth
|
||||
// to create the part.
|
||||
//
|
||||
// Parameters:
|
||||
// - header: A map containing the header fields and their corresponding values for the new part.
|
||||
func (mw *msgWriter) newPart(header map[string][]string) {
|
||||
mw.partWriter, mw.err = mw.multiPartWriter[mw.depth-1].CreatePart(header)
|
||||
}
|
||||
|
||||
// writePart writes the corresponding part to the Msg body
|
||||
// writePart writes the corresponding part to the Msg body.
|
||||
//
|
||||
// This function writes a MIME part to the message body, setting the appropriate headers such
|
||||
// as Content-Type and Content-Transfer-Encoding. It determines the charset for the part,
|
||||
// either using the part's own charset or a fallback charset if none is specified. If the part
|
||||
// is at the top level (depth 0), headers are written directly. For nested parts, it creates
|
||||
// a new MIME part with the provided headers.
|
||||
//
|
||||
// Parameters:
|
||||
// - part: The Part object containing the data to be written.
|
||||
// - charset: The Charset used as a fallback if the part does not specify one.
|
||||
func (mw *msgWriter) writePart(part *Part, charset Charset) {
|
||||
partCharset := part.charset
|
||||
if partCharset.String() == "" {
|
||||
partCharset = charset
|
||||
}
|
||||
|
||||
contentType := fmt.Sprintf("%s; charset=%s", part.contentType, partCharset)
|
||||
if part.smime {
|
||||
contentType = part.contentType.String()
|
||||
}
|
||||
contentTransferEnc := part.encoding.String()
|
||||
|
||||
if mw.depth == 0 {
|
||||
mw.writeHeader(HeaderContentType, contentType)
|
||||
mw.writeHeader(HeaderContentTransferEnc, contentTransferEnc)
|
||||
mw.writeHeader(HeaderContentType, contentType)
|
||||
mw.writeString(SingleNewLine)
|
||||
}
|
||||
if mw.depth > 0 {
|
||||
@@ -265,14 +431,21 @@ func (mw *msgWriter) writePart(part *Part, charset Charset) {
|
||||
if part.description != "" {
|
||||
mimeHeader.Add(string(HeaderContentDescription), part.description)
|
||||
}
|
||||
mimeHeader.Add(string(HeaderContentType), contentType)
|
||||
mimeHeader.Add(string(HeaderContentTransferEnc), contentTransferEnc)
|
||||
mimeHeader.Add(string(HeaderContentType), contentType)
|
||||
mw.newPart(mimeHeader)
|
||||
}
|
||||
mw.writeBody(part.writeFunc, part.encoding)
|
||||
}
|
||||
|
||||
// writeString writes a string into the msgWriter's io.Writer interface
|
||||
// writeString writes a string into the msgWriter's io.Writer interface.
|
||||
//
|
||||
// This function writes the given string to the msgWriter's underlying writer. It checks for
|
||||
// existing errors before performing the write operation. It also tracks the number of bytes
|
||||
// written and updates the bytesWritten field accordingly.
|
||||
//
|
||||
// Parameters:
|
||||
// - s: The string to be written.
|
||||
func (mw *msgWriter) writeString(s string) {
|
||||
if mw.err != nil {
|
||||
return
|
||||
@@ -282,15 +455,25 @@ func (mw *msgWriter) writeString(s string) {
|
||||
mw.bytesWritten += int64(n)
|
||||
}
|
||||
|
||||
// writeHeader writes a header into the msgWriter's io.Writer
|
||||
func (mw *msgWriter) writeHeader(key Header, values ...string) {
|
||||
// writeHeader writes a header into the msgWriter's io.Writer.
|
||||
//
|
||||
// This function writes a header key and its associated values to the msgWriter. It ensures
|
||||
// proper formatting of long headers by inserting line breaks as needed. The header values
|
||||
// are joined and split into words to ensure compliance with the maximum header length
|
||||
// (MaxHeaderLength). After processing the header, it is written to the underlying writer.
|
||||
//
|
||||
// Parameters:
|
||||
// - key: The Header key to be written.
|
||||
// - values: A variadic parameter representing the values associated with the header.
|
||||
func (mw *msgWriter) writeHeader(key Header, values ...string) int {
|
||||
lines := 0
|
||||
buffer := strings.Builder{}
|
||||
charLength := MaxHeaderLength - 2
|
||||
buffer.WriteString(string(key))
|
||||
charLength -= len(key)
|
||||
if len(values) == 0 {
|
||||
buffer.WriteString(":\r\n")
|
||||
return
|
||||
return lines + 1
|
||||
}
|
||||
buffer.WriteString(": ")
|
||||
charLength -= 2
|
||||
@@ -315,9 +498,22 @@ func (mw *msgWriter) writeHeader(key Header, values ...string) {
|
||||
SingleNewLine)
|
||||
mw.writeString(bufferString)
|
||||
mw.writeString("\r\n")
|
||||
|
||||
lines += strings.Count(bufferString, SingleNewLine) + 1
|
||||
return lines
|
||||
}
|
||||
|
||||
// writeBody writes an io.Reader into an io.Writer using provided Encoding
|
||||
// writeBody writes an io.Reader into an io.Writer using the provided Encoding.
|
||||
//
|
||||
// This function writes data from an io.Reader to the underlying writer using a specified
|
||||
// encoding (quoted-printable, base64, or no encoding). It handles encoding of the content
|
||||
// and manages writing the encoded data to the appropriate writer, depending on the depth
|
||||
// (whether the data is part of a multipart structure or not). It also tracks the number
|
||||
// of bytes written and manages any errors encountered during the process.
|
||||
//
|
||||
// Parameters:
|
||||
// - writeFunc: A function that writes the body content to the given io.Writer.
|
||||
// - encoding: The encoding type to use when writing the content (e.g., base64, quoted-printable).
|
||||
func (mw *msgWriter) writeBody(writeFunc func(io.Writer) (int64, error), encoding Encoding) {
|
||||
var writer io.Writer
|
||||
var encodedWriter io.WriteCloser
|
||||
@@ -330,7 +526,7 @@ func (mw *msgWriter) writeBody(writeFunc func(io.Writer) (int64, error), encodin
|
||||
writer = mw.partWriter
|
||||
}
|
||||
writeBuffer := bytes.Buffer{}
|
||||
lineBreaker := Base64LineBreaker{}
|
||||
lineBreaker := base64LineBreaker{}
|
||||
lineBreaker.out = &writeBuffer
|
||||
|
||||
switch encoding {
|
||||
@@ -378,3 +574,33 @@ func (mw *msgWriter) writeBody(writeFunc func(io.Writer) (int64, error), encodin
|
||||
mw.bytesWritten += n
|
||||
}
|
||||
}
|
||||
|
||||
// sanitizeFilename sanitizes a given filename string by replacing specific unwanted characters with
|
||||
// an underscore ('_').
|
||||
//
|
||||
// This method replaces any control character and any special character that is problematic for
|
||||
// MIME headers and file systems with an underscore ('_') character.
|
||||
//
|
||||
// The following characters are replaced
|
||||
// - Any control character (US-ASCII < 32)
|
||||
// - ", /, :, <, >, ?, \, |, [DEL]
|
||||
//
|
||||
// Parameters:
|
||||
// - input: A string of a filename that is supposed to be sanitized
|
||||
//
|
||||
// Returns:
|
||||
// - A string representing the sanitized version of the filename
|
||||
func sanitizeFilename(input string) string {
|
||||
var sanitized strings.Builder
|
||||
for i := 0; i < len(input); i++ {
|
||||
// We do not allow control characters in file names.
|
||||
if input[i] < 32 || input[i] == 34 || input[i] == 47 || input[i] == 58 ||
|
||||
input[i] == 60 || input[i] == 62 || input[i] == 63 || input[i] == 92 ||
|
||||
input[i] == 124 || input[i] == 127 {
|
||||
sanitized.WriteRune('_')
|
||||
continue
|
||||
}
|
||||
sanitized.WriteByte(input[i])
|
||||
}
|
||||
return sanitized.String()
|
||||
}
|
||||
|
||||
+142
-19
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: 2022-2023 The go-mail Authors
|
||||
// SPDX-FileCopyrightText: The go-mail Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
@@ -12,7 +12,11 @@ import (
|
||||
// PartOption returns a function that can be used for grouping Part options
|
||||
type PartOption func(*Part)
|
||||
|
||||
// Part is a part of the Msg
|
||||
// Part is a part of the Msg.
|
||||
//
|
||||
// This struct represents a single part of a multipart message. Each part has a content type,
|
||||
// charset, optional description, encoding, and a function to write its content to an io.Writer.
|
||||
// It also includes a flag to mark the part as deleted.
|
||||
type Part struct {
|
||||
contentType ContentType
|
||||
charset Charset
|
||||
@@ -20,9 +24,17 @@ type Part struct {
|
||||
encoding Encoding
|
||||
isDeleted bool
|
||||
writeFunc func(io.Writer) (int64, error)
|
||||
smime bool
|
||||
}
|
||||
|
||||
// GetContent executes the WriteFunc of the Part and returns the content as byte slice
|
||||
// GetContent executes the WriteFunc of the Part and returns the content as a byte slice.
|
||||
//
|
||||
// This function runs the part's writeFunc to write its content into a buffer and then returns
|
||||
// the content as a byte slice. If an error occurs during the writing process, it is returned.
|
||||
//
|
||||
// Returns:
|
||||
// - A byte slice containing the part's content.
|
||||
// - An error if the writeFunc encounters an issue.
|
||||
func (p *Part) GetContent() ([]byte, error) {
|
||||
var b bytes.Buffer
|
||||
if _, err := p.writeFunc(&b); err != nil {
|
||||
@@ -31,85 +43,196 @@ func (p *Part) GetContent() ([]byte, error) {
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
// GetCharset returns the currently set Charset of the Part
|
||||
// GetCharset returns the currently set Charset of the Part.
|
||||
//
|
||||
// This function returns the Charset that is currently set for the Part.
|
||||
//
|
||||
// Returns:
|
||||
// - The Charset of the Part.
|
||||
func (p *Part) GetCharset() Charset {
|
||||
return p.charset
|
||||
}
|
||||
|
||||
// GetContentType returns the currently set ContentType of the Part
|
||||
// GetContentType returns the currently set ContentType of the Part.
|
||||
//
|
||||
// This function returns the ContentType that is currently set for the Part.
|
||||
//
|
||||
// Returns:
|
||||
// - The ContentType of the Part.
|
||||
func (p *Part) GetContentType() ContentType {
|
||||
return p.contentType
|
||||
}
|
||||
|
||||
// GetEncoding returns the currently set Encoding of the Part
|
||||
// GetEncoding returns the currently set Encoding of the Part.
|
||||
//
|
||||
// This function returns the Encoding that is currently set for the Part.
|
||||
//
|
||||
// Returns:
|
||||
// - The Encoding of the Part.
|
||||
func (p *Part) GetEncoding() Encoding {
|
||||
return p.encoding
|
||||
}
|
||||
|
||||
// GetWriteFunc returns the currently set WriterFunc of the Part
|
||||
// GetWriteFunc returns the currently set WriteFunc of the Part.
|
||||
//
|
||||
// This function returns the WriteFunc that is currently set for the Part, which writes
|
||||
// the part's content to an io.Writer.
|
||||
//
|
||||
// Returns:
|
||||
// - The WriteFunc of the Part, which is a function that takes an io.Writer and returns
|
||||
// the number of bytes written and an error (if any).
|
||||
func (p *Part) GetWriteFunc() func(io.Writer) (int64, error) {
|
||||
return p.writeFunc
|
||||
}
|
||||
|
||||
// GetDescription returns the currently set Content-Description of the Part
|
||||
// GetDescription returns the currently set Content-Description of the Part.
|
||||
//
|
||||
// This function returns the Content-Description that is currently set for the Part.
|
||||
//
|
||||
// Returns:
|
||||
// - The Content-Description of the Part as a string.
|
||||
func (p *Part) GetDescription() string {
|
||||
return p.description
|
||||
}
|
||||
|
||||
// SetContent overrides the content of the Part with the given string
|
||||
// SetContent overrides the content of the Part with the given string.
|
||||
//
|
||||
// This function sets the content of the Part by creating a new writeFunc that writes the
|
||||
// provided string content to an io.Writer.
|
||||
//
|
||||
// Parameters:
|
||||
// - content: The string that will replace the current content of the Part.
|
||||
func (p *Part) SetContent(content string) {
|
||||
buffer := bytes.NewBufferString(content)
|
||||
p.writeFunc = writeFuncFromBuffer(buffer)
|
||||
}
|
||||
|
||||
// SetContentType overrides the ContentType of the Part
|
||||
// SetContentType overrides the ContentType of the Part.
|
||||
//
|
||||
// This function sets a new ContentType for the Part, replacing the existing one.
|
||||
//
|
||||
// Parameters:
|
||||
// - contentType: The new ContentType to be set for the Part.
|
||||
func (p *Part) SetContentType(contentType ContentType) {
|
||||
p.contentType = contentType
|
||||
}
|
||||
|
||||
// SetCharset overrides the Charset of the Part
|
||||
// SetCharset overrides the Charset of the Part.
|
||||
//
|
||||
// This function sets a new Charset for the Part, replacing the existing one.
|
||||
//
|
||||
// Parameters:
|
||||
// - charset: The new Charset to be set for the Part.
|
||||
func (p *Part) SetCharset(charset Charset) {
|
||||
p.charset = charset
|
||||
}
|
||||
|
||||
// SetEncoding creates a new mime.WordEncoder based on the encoding setting of the message
|
||||
// SetEncoding creates a new mime.WordEncoder based on the encoding setting of the message.
|
||||
//
|
||||
// This function sets a new Encoding for the Part, replacing the existing one.
|
||||
//
|
||||
// Parameters:
|
||||
// - encoding: The new Encoding to be set for the Part.
|
||||
func (p *Part) SetEncoding(encoding Encoding) {
|
||||
p.encoding = encoding
|
||||
}
|
||||
|
||||
// SetDescription overrides the Content-Description of the Part
|
||||
// SetDescription overrides the Content-Description of the Part.
|
||||
//
|
||||
// This function sets a new Content-Description for the Part, replacing the existing one.
|
||||
//
|
||||
// Parameters:
|
||||
// - description: The new Content-Description to be set for the Part.
|
||||
func (p *Part) SetDescription(description string) {
|
||||
p.description = description
|
||||
}
|
||||
|
||||
// SetWriteFunc overrides the WriteFunc of the Part
|
||||
// SetIsSMIMESigned sets the flag for signing the Part with S/MIME.
|
||||
//
|
||||
// This function updates the S/MIME signing flag for the Part.
|
||||
//
|
||||
// Parameters:
|
||||
// - smime: A boolean indicating whether the Part should be signed with S/MIME.
|
||||
func (p *Part) SetIsSMIMESigned(smime bool) {
|
||||
p.smime = smime
|
||||
}
|
||||
|
||||
// SetWriteFunc overrides the WriteFunc of the Part.
|
||||
//
|
||||
// This function sets a new WriteFunc for the Part, replacing the existing one. The WriteFunc
|
||||
// is responsible for writing the Part's content to an io.Writer.
|
||||
//
|
||||
// Parameters:
|
||||
// - writeFunc: A function that writes the Part's content to an io.Writer and returns
|
||||
// the number of bytes written and an error (if any).
|
||||
func (p *Part) SetWriteFunc(writeFunc func(io.Writer) (int64, error)) {
|
||||
p.writeFunc = writeFunc
|
||||
}
|
||||
|
||||
// Delete removes the current part from the parts list of the Msg by setting the
|
||||
// isDeleted flag to true. The msgWriter will skip it then
|
||||
// Delete removes the current part from the parts list of the Msg by setting the isDeleted flag to true.
|
||||
//
|
||||
// This function marks the Part as deleted by setting the isDeleted flag to true. The msgWriter
|
||||
// will skip over this Part during processing.
|
||||
func (p *Part) Delete() {
|
||||
p.isDeleted = true
|
||||
}
|
||||
|
||||
// WithPartCharset overrides the default Part charset
|
||||
// WithPartCharset overrides the default Part charset.
|
||||
//
|
||||
// This function returns a PartOption that allows the charset of a Part to be overridden
|
||||
// with the specified Charset.
|
||||
//
|
||||
// Parameters:
|
||||
// - charset: The Charset to be set for the Part.
|
||||
//
|
||||
// Returns:
|
||||
// - A PartOption function that sets the Part's charset.
|
||||
func WithPartCharset(charset Charset) PartOption {
|
||||
return func(p *Part) {
|
||||
p.charset = charset
|
||||
}
|
||||
}
|
||||
|
||||
// WithPartEncoding overrides the default Part encoding
|
||||
// WithPartEncoding overrides the default Part encoding.
|
||||
//
|
||||
// This function returns a PartOption that allows the encoding of a Part to be overridden
|
||||
// with the specified Encoding.
|
||||
//
|
||||
// Parameters:
|
||||
// - encoding: The Encoding to be set for the Part.
|
||||
//
|
||||
// Returns:
|
||||
// - A PartOption function that sets the Part's encoding.
|
||||
func WithPartEncoding(encoding Encoding) PartOption {
|
||||
return func(p *Part) {
|
||||
p.encoding = encoding
|
||||
}
|
||||
}
|
||||
|
||||
// WithPartContentDescription overrides the default Part Content-Description
|
||||
// WithPartContentDescription overrides the default Part Content-Description.
|
||||
//
|
||||
// This function returns a PartOption that allows the Content-Description of a Part
|
||||
// to be overridden with the specified description.
|
||||
//
|
||||
// Parameters:
|
||||
// - description: The Content-Description to be set for the Part.
|
||||
//
|
||||
// Returns:
|
||||
// - A PartOption function that sets the Part's Content-Description.
|
||||
func WithPartContentDescription(description string) PartOption {
|
||||
return func(p *Part) {
|
||||
p.description = description
|
||||
}
|
||||
}
|
||||
|
||||
// WithSMIMESigning enables the S/MIME signing flag for a Part.
|
||||
//
|
||||
// This function provides a PartOption that overrides the S/MIME signing flag to enable signing.
|
||||
//
|
||||
// Returns:
|
||||
// - A PartOption that sets the S/MIME signing flag to true.
|
||||
func WithSMIMESigning() PartOption {
|
||||
return func(p *Part) {
|
||||
p.smime = true
|
||||
}
|
||||
}
|
||||
|
||||
+41
-24
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: 2022-2023 The go-mail Authors
|
||||
// SPDX-FileCopyrightText: The go-mail Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
@@ -8,22 +8,42 @@ import (
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Range of characters for the secure string generation
|
||||
const cr = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
|
||||
const cr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-"
|
||||
|
||||
// Bitmask sizes for the string generators (based on 93 chars total)
|
||||
//
|
||||
// These constants define bitmask-related values used for efficient random string generation.
|
||||
// The bitmask operates over 66 possible characters, and the constants help determine the
|
||||
// number of bits and indices used in the process.
|
||||
const (
|
||||
letterIdxBits = 7 // 7 bits to represent a letter index
|
||||
letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
|
||||
letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
|
||||
// letterIdxBits: Number of bits needed to represent a letter index. We have 64 possible characters
|
||||
// which fit into 6 bits.
|
||||
letterIdxBits = 6
|
||||
// letterIdxMask: Bitmask to extract letter indices (all 1-bits for letterIdxBits).
|
||||
letterIdxMask = 1<<letterIdxBits - 1
|
||||
// letterIdxMax: The maximum number of letter indices that fit in 63 bits.
|
||||
letterIdxMax = 63 / letterIdxBits
|
||||
)
|
||||
|
||||
// randomStringSecure returns a random, string of length characters. This method uses the
|
||||
// crypto/random package and therfore is cryptographically secure
|
||||
// randomStringSecure returns a random string of the specified length.
|
||||
//
|
||||
// This function generates a cryptographically secure random string of the given length using
|
||||
// the crypto/rand package. It ensures that the randomness is secure and suitable for
|
||||
// cryptographic purposes. The function reads random bytes, converts them to indices within
|
||||
// a character range, and builds the string. If an error occurs while reading from the random
|
||||
// pool, it returns the error.
|
||||
//
|
||||
// Parameters:
|
||||
// - length: The length of the random string to be generated.
|
||||
//
|
||||
// Returns:
|
||||
// - A randomly generated string.
|
||||
// - An error if the random generation fails.
|
||||
func randomStringSecure(length int) (string, error) {
|
||||
randString := strings.Builder{}
|
||||
randString.Grow(length)
|
||||
@@ -53,22 +73,19 @@ func randomStringSecure(length int) (string, error) {
|
||||
return randString.String(), nil
|
||||
}
|
||||
|
||||
// randNum returns a random number with a maximum value of length
|
||||
func randNum(length int) (int, error) {
|
||||
if length <= 0 {
|
||||
return 0, fmt.Errorf("provided number is <= 0: %d", length)
|
||||
}
|
||||
length64 := big.NewInt(int64(length))
|
||||
if !length64.IsUint64() {
|
||||
return 0, fmt.Errorf("big.NewInt() generation returned negative value: %d", length64)
|
||||
}
|
||||
randNum64, err := rand.Int(rand.Reader, length64)
|
||||
// randomBoundary generates a random boundary string for use in MIME messages.
|
||||
//
|
||||
// This function creates a 30-byte random value using a cryptographic random number generator
|
||||
// and formats it as a hexadecimal string.
|
||||
//
|
||||
// Returns:
|
||||
// - A string containing the generated random boundary.
|
||||
// - An error if reading from the random number generator fails.
|
||||
func randomBoundary() (string, error) {
|
||||
var buf [30]byte
|
||||
_, err := io.ReadFull(rand.Reader, buf[:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return "", fmt.Errorf("failed to read from rand.Reader: %w", err)
|
||||
}
|
||||
randomNum := int(randNum64.Int64())
|
||||
if randomNum < 0 {
|
||||
return 0, fmt.Errorf("generated random number does not fit as int64: %d", randNum64)
|
||||
}
|
||||
return randomNum, nil
|
||||
return fmt.Sprintf("%x", buf[:]), nil
|
||||
}
|
||||
|
||||
+36
-6
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: 2022-2023 The go-mail Authors
|
||||
// SPDX-FileCopyrightText: The go-mail Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
@@ -8,19 +8,41 @@ import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// Reader is a type that implements the io.Reader interface for a Msg
|
||||
// Reader is a type that implements the io.Reader interface for a Msg.
|
||||
//
|
||||
// This struct represents a reader that reads from a byte slice buffer. It keeps track of the
|
||||
// current read position (offset) and any initialization error. The buffer holds the data to be
|
||||
// read from the message.
|
||||
type Reader struct {
|
||||
buffer []byte // contents are the bytes buffer[offset : len(buffer)]
|
||||
offset int // read at &buffer[offset], write at &buffer[len(buffer)]
|
||||
err error // initialization error
|
||||
}
|
||||
|
||||
// Error returns an error if the Reader err field is not nil
|
||||
// Error returns an error if the Reader err field is not nil.
|
||||
//
|
||||
// This function checks the Reader's err field and returns it if it is not nil. If no error
|
||||
// occurred during initialization, it returns nil.
|
||||
//
|
||||
// Returns:
|
||||
// - The error stored in the err field, or nil if no error is present.
|
||||
func (r *Reader) Error() error {
|
||||
return r.err
|
||||
}
|
||||
|
||||
// Read reads the length of p of the Msg buffer to satisfy the io.Reader interface
|
||||
// Read reads the content of the Msg buffer into the provided payload to satisfy the io.Reader interface.
|
||||
//
|
||||
// This function reads data from the Reader's buffer into the provided byte slice (payload).
|
||||
// It checks for errors or an empty buffer and resets the Reader if necessary. If no data is available,
|
||||
// it returns io.EOF. Otherwise, it copies the content from the buffer into the payload and updates
|
||||
// the read offset.
|
||||
//
|
||||
// Parameters:
|
||||
// - payload: A byte slice where the data will be copied.
|
||||
//
|
||||
// Returns:
|
||||
// - n: The number of bytes copied into the payload.
|
||||
// - err: An error if any issues occurred during the read operation or io.EOF if the buffer is empty.
|
||||
func (r *Reader) Read(payload []byte) (n int, err error) {
|
||||
if r.err != nil {
|
||||
return 0, r.err
|
||||
@@ -37,12 +59,20 @@ func (r *Reader) Read(payload []byte) (n int, err error) {
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Reset resets the Reader buffer to be empty, but it retains the underlying storage
|
||||
// for use by future writes.
|
||||
// Reset resets the Reader buffer to be empty, but it retains the underlying storage for future use.
|
||||
//
|
||||
// This function clears the Reader's buffer by setting its length to 0 and resets the read offset
|
||||
// to the beginning. The underlying storage is retained, allowing future writes to reuse the buffer.
|
||||
func (r *Reader) Reset() {
|
||||
r.buffer = r.buffer[:0]
|
||||
r.offset = 0
|
||||
}
|
||||
|
||||
// empty reports whether the unread portion of the Reader buffer is empty.
|
||||
//
|
||||
// This function checks if the unread portion of the Reader's buffer is empty by comparing
|
||||
// the length of the buffer to the current read offset.
|
||||
//
|
||||
// Returns:
|
||||
// - true if the unread portion is empty, false otherwise.
|
||||
func (r *Reader) empty() bool { return len(r.buffer) <= r.offset }
|
||||
|
||||
+168
-14
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: 2022-2023 The go-mail Authors
|
||||
// SPDX-FileCopyrightText: The go-mail Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
@@ -6,6 +6,8 @@ package mail
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -54,20 +56,36 @@ const (
|
||||
ErrAmbiguous
|
||||
)
|
||||
|
||||
// SendError is an error wrapper for delivery errors of the Msg
|
||||
// SendError is an error wrapper for delivery errors of the Msg.
|
||||
//
|
||||
// This struct represents an error that occurs during the delivery of a message. It holds
|
||||
// details about the affected message, a list of errors, the recipient list, and whether
|
||||
// the error is temporary or permanent. It also includes a reason code for the error.
|
||||
type SendError struct {
|
||||
Reason SendErrReason
|
||||
isTemp bool
|
||||
errlist []error
|
||||
rcpt []string
|
||||
affectedMsg *Msg
|
||||
errcode int
|
||||
enhancedStatusCode string
|
||||
errlist []error
|
||||
isTemp bool
|
||||
rcpt []string
|
||||
Reason SendErrReason
|
||||
}
|
||||
|
||||
// SendErrReason represents a comparable reason on why the delivery failed
|
||||
type SendErrReason int
|
||||
|
||||
// Error implements the error interface for the SendError type
|
||||
// Error implements the error interface for the SendError type.
|
||||
//
|
||||
// This function returns a detailed error message string for the SendError, including the
|
||||
// reason for failure, list of errors, affected recipients, and the message ID of the
|
||||
// affected message (if available). If the reason is unknown (greater than 10), it returns
|
||||
// "unknown reason". The error message is built dynamically based on the content of the
|
||||
// error list, recipient list, and message ID.
|
||||
//
|
||||
// Returns:
|
||||
// - A string representing the error message.
|
||||
func (e *SendError) Error() string {
|
||||
if e.Reason > 10 {
|
||||
if e.Reason > ErrAmbiguous {
|
||||
return "unknown reason"
|
||||
}
|
||||
|
||||
@@ -79,7 +97,7 @@ func (e *SendError) Error() string {
|
||||
errMessage.WriteRune(' ')
|
||||
errMessage.WriteString(e.errlist[i].Error())
|
||||
if i != len(e.errlist)-1 {
|
||||
errMessage.WriteString(", ")
|
||||
errMessage.WriteString(",")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,10 +110,25 @@ func (e *SendError) Error() string {
|
||||
}
|
||||
}
|
||||
}
|
||||
if e.affectedMsg != nil && e.affectedMsg.GetMessageID() != "" {
|
||||
errMessage.WriteString(", affected message ID: ")
|
||||
errMessage.WriteString(e.affectedMsg.GetMessageID())
|
||||
}
|
||||
|
||||
return errMessage.String()
|
||||
}
|
||||
|
||||
// Is implements the errors.Is functionality and compares the SendErrReason
|
||||
// Is implements the errors.Is functionality and compares the SendErrReason.
|
||||
//
|
||||
// This function allows for comparison between two errors by checking if the provided
|
||||
// error matches the SendError type and, if so, compares the SendErrReason and the
|
||||
// temporary status (isTemp) of both errors.
|
||||
//
|
||||
// Parameters:
|
||||
// - errType: The error to compare against the current SendError.
|
||||
//
|
||||
// Returns:
|
||||
// - true if the errors have the same reason and temporary status, false otherwise.
|
||||
func (e *SendError) Is(errType error) bool {
|
||||
var t *SendError
|
||||
if errors.As(errType, &t) && t != nil {
|
||||
@@ -104,7 +137,13 @@ func (e *SendError) Is(errType error) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsTemp returns true if the delivery error is of temporary nature and can be retried
|
||||
// IsTemp returns true if the delivery error is of a temporary nature and can be retried.
|
||||
//
|
||||
// This function checks whether the SendError indicates a temporary error, which suggests
|
||||
// that the delivery can be retried. If the SendError is nil, it returns false.
|
||||
//
|
||||
// Returns:
|
||||
// - true if the error is temporary, false otherwise.
|
||||
func (e *SendError) IsTemp() bool {
|
||||
if e == nil {
|
||||
return false
|
||||
@@ -112,7 +151,78 @@ func (e *SendError) IsTemp() bool {
|
||||
return e.isTemp
|
||||
}
|
||||
|
||||
// String implements the Stringer interface for the SendErrReason
|
||||
// MessageID returns the message ID of the affected Msg that caused the error.
|
||||
//
|
||||
// This function retrieves the message ID of the Msg associated with the SendError.
|
||||
// If no message ID was set or if the SendError or Msg is nil, it returns an empty string.
|
||||
//
|
||||
// Returns:
|
||||
// - The message ID as a string, or an empty string if no ID is available.
|
||||
func (e *SendError) MessageID() string {
|
||||
if e == nil || e.affectedMsg == nil {
|
||||
return ""
|
||||
}
|
||||
return e.affectedMsg.GetMessageID()
|
||||
}
|
||||
|
||||
// Msg returns the pointer to the affected message that caused the error.
|
||||
//
|
||||
// This function retrieves the Msg associated with the SendError. If the SendError or
|
||||
// the affectedMsg is nil, it returns nil.
|
||||
//
|
||||
// Returns:
|
||||
// - A pointer to the Msg that caused the error, or nil if not available.
|
||||
func (e *SendError) Msg() *Msg {
|
||||
if e == nil || e.affectedMsg == nil {
|
||||
return nil
|
||||
}
|
||||
return e.affectedMsg
|
||||
}
|
||||
|
||||
// EnhancedStatusCode returns the enhanced status code of the server response if the
|
||||
// server supports it, as described in RFC 2034.
|
||||
//
|
||||
// This function retrieves the enhanced status code of an error returned by the server. This
|
||||
// requires that the receiving server supports this SMTP extension as described in RFC 2034.
|
||||
// Since this is the SendError interface, we only collect status codes for error responses,
|
||||
// meaning 4xx or 5xx. If the server does not support the ENHANCEDSTATUSCODES extension or
|
||||
// the error did not include an enhanced status code, it will return an empty string.
|
||||
//
|
||||
// Returns:
|
||||
// - The enhanced status code as returned by the server, or an empty string is not supported.
|
||||
//
|
||||
// References:
|
||||
// - https://datatracker.ietf.org/doc/html/rfc2034
|
||||
func (e *SendError) EnhancedStatusCode() string {
|
||||
if e == nil {
|
||||
return ""
|
||||
}
|
||||
return e.enhancedStatusCode
|
||||
}
|
||||
|
||||
// ErrorCode returns the error code of the server response.
|
||||
//
|
||||
// This function retrieves the error code the error returned by the server. The error code will
|
||||
// start with 5 on permanent errors and with 4 on a temporary error. If the error is not returned
|
||||
// by the server, but is generated by go-mail, the code will be 0.
|
||||
//
|
||||
// Returns:
|
||||
// - The error code as returned by the server, or 0 if not a server error.
|
||||
func (e *SendError) ErrorCode() int {
|
||||
if e == nil {
|
||||
return 0
|
||||
}
|
||||
return e.errcode
|
||||
}
|
||||
|
||||
// String satisfies the fmt.Stringer interface for the SendErrReason type.
|
||||
//
|
||||
// This function converts the SendErrReason into a human-readable string representation based
|
||||
// on the error type. If the error reason does not match any predefined case, it returns
|
||||
// "unknown reason".
|
||||
//
|
||||
// Returns:
|
||||
// - A string representation of the SendErrReason.
|
||||
func (r SendErrReason) String() string {
|
||||
switch r {
|
||||
case ErrGetSender:
|
||||
@@ -141,8 +251,52 @@ func (r SendErrReason) String() string {
|
||||
return "unknown reason"
|
||||
}
|
||||
|
||||
// isTempError checks the given SMTP error and returns true if the given error is of temporary nature
|
||||
// and should be retried
|
||||
// isTempError checks if the given SMTP error is of a temporary nature and should be retried.
|
||||
//
|
||||
// This function inspects the error message and returns true if the first character of the
|
||||
// error message is '4', indicating a temporary SMTP error that can be retried.
|
||||
//
|
||||
// Parameters:
|
||||
// - err: The error to check.
|
||||
//
|
||||
// Returns:
|
||||
// - true if the error is temporary, false otherwise.
|
||||
func isTempError(err error) bool {
|
||||
return err.Error()[0] == '4'
|
||||
}
|
||||
|
||||
func errorCode(err error) int {
|
||||
rootErr := errors.Unwrap(err)
|
||||
if rootErr != nil {
|
||||
err = rootErr
|
||||
}
|
||||
firstrune := err.Error()[0]
|
||||
if firstrune < 52 || firstrune > 53 {
|
||||
return 0
|
||||
}
|
||||
code := err.Error()[0:3]
|
||||
errcode, cerr := strconv.Atoi(code)
|
||||
if cerr != nil {
|
||||
return 0
|
||||
}
|
||||
return errcode
|
||||
}
|
||||
|
||||
func enhancedStatusCode(err error, supported bool) string {
|
||||
if err == nil || !supported {
|
||||
return ""
|
||||
}
|
||||
rootErr := errors.Unwrap(err)
|
||||
if rootErr != nil {
|
||||
err = rootErr
|
||||
}
|
||||
firstrune := err.Error()[0]
|
||||
if firstrune != 50 && firstrune != 52 && firstrune != 53 {
|
||||
return ""
|
||||
}
|
||||
re, rerr := regexp.Compile(`\b([245])\.\d{1,3}\.\d{1,3}\b`)
|
||||
if rerr != nil {
|
||||
return ""
|
||||
}
|
||||
return re.FindString(err.Error())
|
||||
}
|
||||
|
||||
+14
-1
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2010 The Go Authors. All rights reserved.
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2022-2023 The go-mail Authors
|
||||
// SPDX-FileCopyrightText: Copyright (c) The go-mail Authors
|
||||
//
|
||||
// Original net/smtp code from the Go stdlib by the Go Authors.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
@@ -13,6 +13,19 @@
|
||||
|
||||
package smtp
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
// ErrUnencrypted is an error indicating that the connection is not encrypted.
|
||||
ErrUnencrypted = errors.New("unencrypted connection")
|
||||
// ErrUnexpectedServerChallange is an error indicating that the server issued an unexpected challenge.
|
||||
ErrUnexpectedServerChallange = errors.New("unexpected server challenge")
|
||||
// ErrUnexpectedServerResponse is an error indicating that the server issued an unexpected response.
|
||||
ErrUnexpectedServerResponse = errors.New("unexpected server response")
|
||||
// ErrWrongHostname is an error indicating that the provided hostname does not match the expected value.
|
||||
ErrWrongHostname = errors.New("wrong host name")
|
||||
)
|
||||
|
||||
// Auth is implemented by an SMTP authentication mechanism.
|
||||
type Auth interface {
|
||||
// Start begins an authentication with a server.
|
||||
|
||||
+1
-4
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2010 The Go Authors. All rights reserved.
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2022-2023 The go-mail Authors
|
||||
// SPDX-FileCopyrightText: Copyright (c) The go-mail Authors
|
||||
//
|
||||
// Original net/smtp code from the Go stdlib by the Go Authors.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
@@ -11,9 +11,6 @@
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-3-Clause AND MIT
|
||||
|
||||
//go:build go1.19
|
||||
// +build go1.19
|
||||
|
||||
package smtp
|
||||
|
||||
import (
|
||||
|
||||
-52
@@ -1,52 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2010 The Go Authors. All rights reserved.
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2022-2023 The go-mail Authors
|
||||
//
|
||||
// Original net/smtp code from the Go stdlib by the Go Authors.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// LICENSE file that can be found in this directory.
|
||||
//
|
||||
// go-mail specific modifications by the go-mail Authors.
|
||||
// Licensed under the MIT License.
|
||||
// See [PROJECT ROOT]/LICENSES directory for more information.
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-3-Clause AND MIT
|
||||
|
||||
//go:build !go1.19
|
||||
// +build !go1.19
|
||||
|
||||
package smtp
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// cramMD5Auth is the type that satisfies the Auth interface for the "SMTP CRAM_MD5" auth
|
||||
type cramMD5Auth struct {
|
||||
username, secret string
|
||||
}
|
||||
|
||||
// CRAMMD5Auth returns an [Auth] that implements the CRAM-MD5 authentication
|
||||
// mechanism as defined in RFC 2195.
|
||||
// The returned Auth uses the given username and secret to authenticate
|
||||
// to the server using the challenge-response mechanism.
|
||||
func CRAMMD5Auth(username, secret string) Auth {
|
||||
return &cramMD5Auth{username, secret}
|
||||
}
|
||||
|
||||
func (a *cramMD5Auth) Start(_ *ServerInfo) (string, []byte, error) {
|
||||
return "CRAM-MD5", nil, nil
|
||||
}
|
||||
|
||||
// Backport of: https://github.com/golang/go/commit/58158e990f272774e615c9abd8662bf0198c29aa#diff-772fc9f5d0c86f26e35158fb3e7a71a4967d18b4ec23a5dbb60781ab0babf426
|
||||
// to guarantee backwards compatibility with Go 1.16-1.18
|
||||
func (a *cramMD5Auth) Next(fromServer []byte, more bool) ([]byte, error) {
|
||||
if more {
|
||||
d := hmac.New(md5.New, []byte(a.secret))
|
||||
d.Write(fromServer)
|
||||
s := make([]byte, 0, d.Size())
|
||||
return []byte(fmt.Sprintf("%s %x", a.username, d.Sum(s))), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
+32
-43
@@ -1,87 +1,76 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2022-2023 The go-mail Authors
|
||||
// SPDX-FileCopyrightText: Copyright (c) The go-mail Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package smtp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// loginAuth is the type that satisfies the Auth interface for the "SMTP LOGIN" auth
|
||||
type loginAuth struct {
|
||||
username, password string
|
||||
host string
|
||||
username, password string
|
||||
host string
|
||||
respStep uint8
|
||||
allowUnencryptedAuth bool
|
||||
}
|
||||
|
||||
const (
|
||||
// LoginXUsernameChallenge represents the Username Challenge response sent by the SMTP server per the AUTH LOGIN
|
||||
// extension.
|
||||
//
|
||||
// See: https://learn.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-xlogin/.
|
||||
LoginXUsernameChallenge = "Username:"
|
||||
|
||||
// LoginXPasswordChallenge represents the Password Challenge response sent by the SMTP server per the AUTH LOGIN
|
||||
// extension.
|
||||
//
|
||||
// See: https://learn.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-xlogin/.
|
||||
LoginXPasswordChallenge = "Password:"
|
||||
|
||||
// LoginXDraftUsernameChallenge represents the Username Challenge response sent by the SMTP server per the IETF
|
||||
// draft AUTH LOGIN extension. It should be noted this extension is an expired draft which was never formally
|
||||
// published and was deprecated in favor of the AUTH PLAIN extension.
|
||||
//
|
||||
// See: https://datatracker.ietf.org/doc/html/draft-murchison-sasl-login-00.
|
||||
LoginXDraftUsernameChallenge = "User Name\x00"
|
||||
|
||||
// LoginXDraftPasswordChallenge represents the Password Challenge response sent by the SMTP server per the IETF
|
||||
// draft AUTH LOGIN extension. It should be noted this extension is an expired draft which was never formally
|
||||
// published and was deprecated in favor of the AUTH PLAIN extension.
|
||||
//
|
||||
// See: https://datatracker.ietf.org/doc/html/draft-murchison-sasl-login-00.
|
||||
LoginXDraftPasswordChallenge = "Password\x00"
|
||||
)
|
||||
|
||||
// LoginAuth returns an [Auth] that implements the LOGIN authentication
|
||||
// mechanism as it is used by MS Outlook. The Auth works similar to PLAIN
|
||||
// but instead of sending all in one response, the login is handled within
|
||||
// 3 steps:
|
||||
// - Sending AUTH LOGIN (server responds with "Username:")
|
||||
// - Sending the username (server responds with "Password:")
|
||||
// - Sending AUTH LOGIN (server might responds with "Username:")
|
||||
// - Sending the username (server might responds with "Password:")
|
||||
// - Sending the password (server authenticates)
|
||||
// This is the common approach as specified by Microsoft in their MS-XLOGIN spec.
|
||||
// See: https://msopenspecs.azureedge.net/files/MS-XLOGIN/%5bMS-XLOGIN%5d.pdf
|
||||
// Yet, there is also an old IETF draft for SMTP AUTH LOGIN that states for clients:
|
||||
// "The contents of both challenges SHOULD be ignored.".
|
||||
// See: https://datatracker.ietf.org/doc/html/draft-murchison-sasl-login-00
|
||||
// Since there is no official standard RFC and we've seen different implementations
|
||||
// of this mechanism (sending "Username:", "Username", "username", "User name", etc.)
|
||||
// we follow the IETF-Draft and ignore any server challenge to allow compatibility
|
||||
// with most mail servers/providers.
|
||||
//
|
||||
// LoginAuth will only send the credentials if the connection is using TLS
|
||||
// or is connected to localhost. Otherwise authentication will fail with an
|
||||
// error, without sending the credentials.
|
||||
func LoginAuth(username, password, host string) Auth {
|
||||
return &loginAuth{username, password, host}
|
||||
func LoginAuth(username, password, host string, allowUnenc bool) Auth {
|
||||
return &loginAuth{username, password, host, 0, allowUnenc}
|
||||
}
|
||||
|
||||
// Start begins the SMTP authentication process by validating server's TLS status and hostname.
|
||||
// Returns "LOGIN" on success.
|
||||
func (a *loginAuth) Start(server *ServerInfo) (string, []byte, error) {
|
||||
// Must have TLS, or else localhost server.
|
||||
// Note: If TLS is not true, then we can't trust ANYTHING in ServerInfo.
|
||||
// In particular, it doesn't matter if the server advertises LOGIN auth.
|
||||
// That might just be the attacker saying
|
||||
// "it's ok, you can trust me with your password."
|
||||
if !server.TLS && !isLocalhost(server.Name) {
|
||||
return "", nil, errors.New("unencrypted connection")
|
||||
if !a.allowUnencryptedAuth && !server.TLS && !isLocalhost(server.Name) {
|
||||
return "", nil, ErrUnencrypted
|
||||
}
|
||||
if server.Name != a.host {
|
||||
return "", nil, errors.New("wrong host name")
|
||||
return "", nil, ErrWrongHostname
|
||||
}
|
||||
a.respStep = 0
|
||||
return "LOGIN", nil, nil
|
||||
}
|
||||
|
||||
// Next processes responses from the server during the SMTP authentication exchange, sending the
|
||||
// username and password.
|
||||
func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
|
||||
if more {
|
||||
switch string(fromServer) {
|
||||
case LoginXUsernameChallenge, LoginXDraftUsernameChallenge:
|
||||
switch a.respStep {
|
||||
case 0:
|
||||
a.respStep++
|
||||
return []byte(a.username), nil
|
||||
case LoginXPasswordChallenge, LoginXDraftPasswordChallenge:
|
||||
case 1:
|
||||
a.respStep++
|
||||
return []byte(a.password), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected server response: %s", string(fromServer))
|
||||
return nil, fmt.Errorf("%w: %s", ErrUnexpectedServerResponse, string(fromServer))
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
|
||||
+8
-11
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2010 The Go Authors. All rights reserved.
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2022-2023 The go-mail Authors
|
||||
// SPDX-FileCopyrightText: Copyright (c) The go-mail Authors
|
||||
//
|
||||
// Original net/smtp code from the Go stdlib by the Go Authors.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
@@ -13,14 +13,11 @@
|
||||
|
||||
package smtp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// plainAuth is the type that satisfies the Auth interface for the "SMTP PLAIN" auth
|
||||
type plainAuth struct {
|
||||
identity, username, password string
|
||||
host string
|
||||
allowUnencryptedAuth bool
|
||||
}
|
||||
|
||||
// PlainAuth returns an [Auth] that implements the PLAIN authentication
|
||||
@@ -31,8 +28,8 @@ type plainAuth struct {
|
||||
// PlainAuth will only send the credentials if the connection is using TLS
|
||||
// or is connected to localhost. Otherwise authentication will fail with an
|
||||
// error, without sending the credentials.
|
||||
func PlainAuth(identity, username, password, host string) Auth {
|
||||
return &plainAuth{identity, username, password, host}
|
||||
func PlainAuth(identity, username, password, host string, allowUnenc bool) Auth {
|
||||
return &plainAuth{identity, username, password, host, allowUnenc}
|
||||
}
|
||||
|
||||
func (a *plainAuth) Start(server *ServerInfo) (string, []byte, error) {
|
||||
@@ -41,11 +38,11 @@ func (a *plainAuth) Start(server *ServerInfo) (string, []byte, error) {
|
||||
// In particular, it doesn't matter if the server advertises PLAIN auth.
|
||||
// That might just be the attacker saying
|
||||
// "it's ok, you can trust me with your password."
|
||||
if !server.TLS && !isLocalhost(server.Name) {
|
||||
return "", nil, errors.New("unencrypted connection")
|
||||
if !a.allowUnencryptedAuth && !server.TLS && !isLocalhost(server.Name) {
|
||||
return "", nil, ErrUnencrypted
|
||||
}
|
||||
if server.Name != a.host {
|
||||
return "", nil, errors.New("wrong host name")
|
||||
return "", nil, ErrWrongHostname
|
||||
}
|
||||
resp := []byte(a.identity + "\x00" + a.username + "\x00" + a.password)
|
||||
return "PLAIN", resp, nil
|
||||
@@ -54,7 +51,7 @@ func (a *plainAuth) Start(server *ServerInfo) (string, []byte, error) {
|
||||
func (a *plainAuth) Next(_ []byte, more bool) ([]byte, error) {
|
||||
if more {
|
||||
// We've already sent everything.
|
||||
return nil, errors.New("unexpected server challenge")
|
||||
return nil, ErrUnexpectedServerChallange
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2023 The go-mail Authors
|
||||
// SPDX-FileCopyrightText: Copyright (c) The go-mail Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
|
||||
+253
-39
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2010 The Go Authors. All rights reserved.
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2022-2023 The go-mail Authors
|
||||
// SPDX-FileCopyrightText: Copyright (c) The go-mail Authors
|
||||
//
|
||||
// Original net/smtp code from the Go stdlib by the Go Authors.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
@@ -30,34 +30,79 @@ import (
|
||||
"net/textproto"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/wneessen/go-mail/log"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
// ErrNonTLSConnection is returned when an attempt is made to retrieve TLS state on a non-TLS connection.
|
||||
ErrNonTLSConnection = errors.New("connection is not using TLS")
|
||||
|
||||
// ErrNoConnection is returned when attempting to perform an operation that requires an established
|
||||
// connection but none exists.
|
||||
ErrNoConnection = errors.New("connection is not established")
|
||||
)
|
||||
|
||||
// A Client represents a client connection to an SMTP server.
|
||||
type Client struct {
|
||||
// Text is the textproto.Conn used by the Client. It is exported to allow for
|
||||
// clients to add extensions.
|
||||
// Text is the textproto.Conn used by the Client. It is exported to allow for clients to add extensions.
|
||||
Text *textproto.Conn
|
||||
// keep a reference to the connection so it can be used to create a TLS
|
||||
// connection later
|
||||
|
||||
// ErrorHandlerRegistry manages custom error handlers for SMTP host-command pairs.
|
||||
ErrorHandlerRegistry *ErrorHandlerRegistry
|
||||
|
||||
// auth supported auth mechanisms
|
||||
auth []string
|
||||
|
||||
// authIsActive indicates that the Client is currently during SMTP authentication
|
||||
authIsActive bool
|
||||
|
||||
// keep a reference to the connection so it can be used to create a TLS connection later
|
||||
conn net.Conn
|
||||
// whether the Client is using TLS
|
||||
tls bool
|
||||
serverName string
|
||||
// map of supported extensions
|
||||
|
||||
// debug logging is enabled
|
||||
debug bool
|
||||
|
||||
// didHello indicates whether we've said HELO/EHLO
|
||||
didHello bool
|
||||
|
||||
// dsnmrtype defines the mail return option in case DSN is enabled
|
||||
dsnmrtype string
|
||||
|
||||
// dsnrntype defines the recipient notify option in case DSN is enabled
|
||||
dsnrntype string
|
||||
|
||||
// ext is a map of supported extensions
|
||||
ext map[string]string
|
||||
// supported auth mechanisms
|
||||
auth []string
|
||||
localName string // the name to use in HELO/EHLO
|
||||
didHello bool // whether we've said HELO/EHLO
|
||||
helloError error // the error from the hello
|
||||
// debug logging
|
||||
debug bool // debug logging is enabled
|
||||
logger log.Logger // logger will be used for debug logging
|
||||
// DSN support
|
||||
dsnmrtype string // dsnmrtype defines the mail return option in case DSN is enabled
|
||||
dsnrntype string // dsnrntype defines the recipient notify option in case DSN is enabled
|
||||
|
||||
// helloError is the error from the hello
|
||||
helloError error
|
||||
|
||||
// isConnected indicates if the Client has an active connection
|
||||
isConnected bool
|
||||
|
||||
// logAuthData indicates if the Client should include SMTP authentication data in the logs
|
||||
logAuthData bool
|
||||
|
||||
// localName is the name to use in HELO/EHLO
|
||||
localName string // the name to use in HELO/EHLO
|
||||
|
||||
// logger will be used for debug logging
|
||||
logger log.Logger
|
||||
|
||||
// mutex is used to synchronize access to shared resources, ensuring that only one goroutine can access
|
||||
// the resource at a time.
|
||||
mutex sync.RWMutex
|
||||
|
||||
// tls indicates whether the Client is using TLS
|
||||
tls bool
|
||||
|
||||
// serverName denotes the name of the server to which the application will connect. Used for
|
||||
// identification and routing.
|
||||
serverName string
|
||||
}
|
||||
|
||||
// Dial returns a new [Client] connected to an SMTP server at addr.
|
||||
@@ -88,13 +133,19 @@ func NewClient(conn net.Conn, host string) (*Client, error) {
|
||||
}
|
||||
c := &Client{Text: text, conn: conn, serverName: host, localName: "localhost"}
|
||||
_, c.tls = conn.(*tls.Conn)
|
||||
c.isConnected = true
|
||||
c.ErrorHandlerRegistry = NewErrorHandlerRegistry()
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Close closes the connection.
|
||||
func (c *Client) Close() error {
|
||||
return c.Text.Close()
|
||||
c.mutex.Lock()
|
||||
err := c.Text.Close()
|
||||
c.isConnected = false
|
||||
c.mutex.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
// hello runs a hello exchange if needed.
|
||||
@@ -121,28 +172,67 @@ func (c *Client) Hello(localName string) error {
|
||||
if c.didHello {
|
||||
return errors.New("smtp: Hello called after other methods")
|
||||
}
|
||||
|
||||
c.mutex.Lock()
|
||||
c.localName = localName
|
||||
c.mutex.Unlock()
|
||||
|
||||
return c.hello()
|
||||
}
|
||||
|
||||
// cmd is a convenience function that sends a command and returns the response
|
||||
func (c *Client) cmd(expectCode int, format string, args ...interface{}) (int, string, error) {
|
||||
c.debugLog(log.DirClientToServer, format, args...)
|
||||
c.mutex.Lock()
|
||||
|
||||
var logMsg []interface{}
|
||||
logMsg = args
|
||||
logFmt := format
|
||||
if c.authIsActive {
|
||||
logMsg = []interface{}{"<SMTP auth data redacted>"}
|
||||
logFmt = "%s"
|
||||
}
|
||||
c.debugLog(log.DirClientToServer, logFmt, logMsg...)
|
||||
|
||||
id, err := c.Text.Cmd(format, args...)
|
||||
if err != nil {
|
||||
c.mutex.Unlock()
|
||||
return 0, "", err
|
||||
}
|
||||
c.Text.StartResponse(id)
|
||||
defer c.Text.EndResponse(id)
|
||||
code, msg, err := c.Text.ReadResponse(expectCode)
|
||||
c.debugLog(log.DirServerToClient, "%d %s", code, msg)
|
||||
if err != nil {
|
||||
fmtValues := strings.Split(format, " ")
|
||||
currentCmd := strings.ToLower(fmtValues[0])
|
||||
handler := c.ErrorHandlerRegistry.GetHandler(c.serverName, currentCmd)
|
||||
handledErr := handler.HandleError(c.serverName, currentCmd, c.Text, err)
|
||||
if handledErr != nil {
|
||||
c.mutex.Unlock()
|
||||
return 0, "", handledErr
|
||||
}
|
||||
|
||||
// If the handler successfully recovered, we try reading the response again
|
||||
// This assumes the handler has consumed the problematic data beforehand.
|
||||
code, msg, err = c.Text.ReadResponse(expectCode)
|
||||
}
|
||||
|
||||
logMsg = []interface{}{code, msg}
|
||||
if c.authIsActive && code >= 300 && code <= 400 {
|
||||
logMsg = []interface{}{code, "<SMTP auth data redacted>"}
|
||||
}
|
||||
c.debugLog(log.DirServerToClient, "%d %s", logMsg...)
|
||||
|
||||
c.mutex.Unlock()
|
||||
return code, msg, err
|
||||
}
|
||||
|
||||
// helo sends the HELO greeting to the server. It should be used only when the
|
||||
// server does not support ehlo.
|
||||
func (c *Client) helo() error {
|
||||
c.mutex.Lock()
|
||||
c.ext = nil
|
||||
c.mutex.Unlock()
|
||||
|
||||
_, _, err := c.cmd(250, "HELO %s", c.localName)
|
||||
return err
|
||||
}
|
||||
@@ -157,9 +247,13 @@ func (c *Client) StartTLS(config *tls.Config) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.mutex.Lock()
|
||||
c.conn = tls.Client(c.conn, config)
|
||||
c.Text = textproto.NewConn(c.conn)
|
||||
c.tls = true
|
||||
c.mutex.Unlock()
|
||||
|
||||
return c.ehlo()
|
||||
}
|
||||
|
||||
@@ -167,11 +261,15 @@ func (c *Client) StartTLS(config *tls.Config) error {
|
||||
// The return values are their zero values if [Client.StartTLS] did
|
||||
// not succeed.
|
||||
func (c *Client) TLSConnectionState() (state tls.ConnectionState, ok bool) {
|
||||
c.mutex.RLock()
|
||||
defer c.mutex.RUnlock()
|
||||
|
||||
tc, ok := c.conn.(*tls.Conn)
|
||||
if !ok {
|
||||
return
|
||||
return state, ok
|
||||
}
|
||||
return tc.ConnectionState(), true
|
||||
state, ok = tc.ConnectionState(), true
|
||||
return state, ok
|
||||
}
|
||||
|
||||
// Verify checks the validity of an email address on the server.
|
||||
@@ -196,6 +294,20 @@ func (c *Client) Auth(a Auth) error {
|
||||
if err := c.hello(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.mutex.Lock()
|
||||
if !c.logAuthData {
|
||||
c.authIsActive = true
|
||||
}
|
||||
c.mutex.Unlock()
|
||||
defer func() {
|
||||
c.mutex.Lock()
|
||||
if !c.logAuthData {
|
||||
c.authIsActive = false
|
||||
}
|
||||
c.mutex.Unlock()
|
||||
}()
|
||||
|
||||
encoding := base64.StdEncoding
|
||||
mech, resp, err := a.Start(&ServerInfo{c.serverName, c.tls, c.auth})
|
||||
if err != nil {
|
||||
@@ -210,7 +322,8 @@ func (c *Client) Auth(a Auth) error {
|
||||
}
|
||||
resp64 := make([]byte, encoding.EncodedLen(len(resp)))
|
||||
encoding.Encode(resp64, resp)
|
||||
code, msg64, err := c.cmd(0, strings.TrimSpace(fmt.Sprintf("AUTH %s %s", mech, resp64)))
|
||||
code, msg64, err := c.cmd(0, "%s", strings.TrimSpace(fmt.Sprintf("AUTH %s %s", mech,
|
||||
resp64)))
|
||||
for err == nil {
|
||||
var msg []byte
|
||||
switch code {
|
||||
@@ -238,7 +351,7 @@ func (c *Client) Auth(a Auth) error {
|
||||
}
|
||||
resp64 = make([]byte, encoding.EncodedLen(len(resp)))
|
||||
encoding.Encode(resp64, resp)
|
||||
code, msg64, err = c.cmd(0, string(resp64))
|
||||
code, msg64, err = c.cmd(0, "%s", resp64)
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -255,7 +368,9 @@ func (c *Client) Mail(from string) error {
|
||||
if err := c.hello(); err != nil {
|
||||
return err
|
||||
}
|
||||
cmdStr := "MAIL FROM:<%s>"
|
||||
cmdStr := "MAIL FROM:%s"
|
||||
|
||||
c.mutex.RLock()
|
||||
if c.ext != nil {
|
||||
if _, ok := c.ext["8BITMIME"]; ok {
|
||||
cmdStr += " BODY=8BITMIME"
|
||||
@@ -268,6 +383,8 @@ func (c *Client) Mail(from string) error {
|
||||
cmdStr += fmt.Sprintf(" RET=%s", c.dsnmrtype)
|
||||
}
|
||||
}
|
||||
c.mutex.RUnlock()
|
||||
|
||||
_, _, err := c.cmd(250, cmdStr, from)
|
||||
return err
|
||||
}
|
||||
@@ -279,26 +396,54 @@ func (c *Client) Rcpt(to string) error {
|
||||
if err := validateLine(to); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.mutex.RLock()
|
||||
_, ok := c.ext["DSN"]
|
||||
c.mutex.RUnlock()
|
||||
|
||||
if ok && c.dsnrntype != "" {
|
||||
_, _, err := c.cmd(25, "RCPT TO:<%s> NOTIFY=%s", to, c.dsnrntype)
|
||||
_, _, err := c.cmd(25, "RCPT TO:%s NOTIFY=%s", to, c.dsnrntype)
|
||||
return err
|
||||
}
|
||||
_, _, err := c.cmd(25, "RCPT TO:<%s>", to)
|
||||
_, _, err := c.cmd(25, "RCPT TO:%s", to)
|
||||
return err
|
||||
}
|
||||
|
||||
type dataCloser struct {
|
||||
c *Client
|
||||
type DataCloser struct {
|
||||
c *Client
|
||||
done bool
|
||||
io.WriteCloser
|
||||
response string
|
||||
}
|
||||
|
||||
func (d *dataCloser) Close() error {
|
||||
// Close releases the lock, closes the WriteCloser, waits for a response, and then returns any error encountered.
|
||||
func (d *DataCloser) Close() error {
|
||||
d.c.mutex.Lock()
|
||||
_ = d.WriteCloser.Close()
|
||||
_, _, err := d.c.Text.ReadResponse(250)
|
||||
_, resp, err := d.c.Text.ReadResponse(250)
|
||||
d.response = resp
|
||||
d.done = true
|
||||
d.c.mutex.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
// Write writes data to the underlying WriteCloser while ensuring thread-safety by locking and unlocking a mutex.
|
||||
func (d *DataCloser) Write(p []byte) (n int, err error) {
|
||||
d.c.mutex.Lock()
|
||||
n, err = d.WriteCloser.Write(p)
|
||||
d.c.mutex.Unlock()
|
||||
return n, err
|
||||
}
|
||||
|
||||
// ServerResponse returns the response that was returned by the server after the DataCloser has
|
||||
// been closed. If the DataCloser has not been closed yet, it will return an empty string.
|
||||
func (d *DataCloser) ServerResponse() string {
|
||||
if !d.done {
|
||||
return ""
|
||||
}
|
||||
return d.response
|
||||
}
|
||||
|
||||
// Data issues a DATA command to the server and returns a writer that
|
||||
// can be used to write the mail headers and body. The caller should
|
||||
// close the writer before calling any more methods on c. A call to
|
||||
@@ -308,7 +453,14 @@ func (c *Client) Data() (io.WriteCloser, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &dataCloser{c, c.Text.DotWriter()}, nil
|
||||
datacloser := &DataCloser{}
|
||||
|
||||
c.mutex.Lock()
|
||||
datacloser.c = c
|
||||
datacloser.WriteCloser = c.Text.DotWriter()
|
||||
c.mutex.Unlock()
|
||||
|
||||
return datacloser, nil
|
||||
}
|
||||
|
||||
var testHookStartTLS func(*tls.Config) // nil, except for tests
|
||||
@@ -404,7 +556,10 @@ func (c *Client) Extension(ext string) (bool, string) {
|
||||
return false, ""
|
||||
}
|
||||
ext = strings.ToUpper(ext)
|
||||
|
||||
c.mutex.RLock()
|
||||
param, ok := c.ext[ext]
|
||||
c.mutex.RUnlock()
|
||||
return ok, param
|
||||
}
|
||||
|
||||
@@ -430,14 +585,19 @@ func (c *Client) Noop() error {
|
||||
|
||||
// Quit sends the QUIT command and closes the connection to the server.
|
||||
func (c *Client) Quit() error {
|
||||
if err := c.hello(); err != nil {
|
||||
return err
|
||||
}
|
||||
// See https://github.com/golang/go/issues/70011
|
||||
_ = c.hello() // ignore error; we're quitting anyhow
|
||||
|
||||
_, _, err := c.cmd(221, "QUIT")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Text.Close()
|
||||
c.mutex.Lock()
|
||||
err = c.Text.Close()
|
||||
c.isConnected = false
|
||||
c.mutex.Unlock()
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// SetDebugLog enables the debug logging for incoming and outgoing SMTP messages
|
||||
@@ -458,17 +618,71 @@ func (c *Client) SetLogger(l log.Logger) {
|
||||
if l == nil {
|
||||
return
|
||||
}
|
||||
c.mutex.Lock()
|
||||
c.logger = l
|
||||
c.mutex.Unlock()
|
||||
}
|
||||
|
||||
// SetLogAuthData enables logging of authentication data in the Client.
|
||||
func (c *Client) SetLogAuthData() {
|
||||
c.mutex.Lock()
|
||||
c.logAuthData = true
|
||||
c.mutex.Unlock()
|
||||
}
|
||||
|
||||
// SetDSNMailReturnOption sets the DSN mail return option for the Mail method
|
||||
func (c *Client) SetDSNMailReturnOption(d string) {
|
||||
c.mutex.Lock()
|
||||
c.dsnmrtype = d
|
||||
c.mutex.Unlock()
|
||||
}
|
||||
|
||||
// SetDSNRcptNotifyOption sets the DSN recipient notify option for the Mail method
|
||||
func (c *Client) SetDSNRcptNotifyOption(d string) {
|
||||
c.mutex.Lock()
|
||||
c.dsnrntype = d
|
||||
c.mutex.Unlock()
|
||||
}
|
||||
|
||||
// HasConnection checks if the client has an active connection.
|
||||
// Returns true if the `conn` field is not nil, indicating an active connection.
|
||||
func (c *Client) HasConnection() bool {
|
||||
c.mutex.RLock()
|
||||
isConn := c.isConnected
|
||||
c.mutex.RUnlock()
|
||||
return isConn
|
||||
}
|
||||
|
||||
// UpdateDeadline sets a new deadline on the SMTP connection with the specified timeout duration.
|
||||
func (c *Client) UpdateDeadline(timeout time.Duration) error {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
if c.conn == nil {
|
||||
return errors.New("smtp: client has no connection")
|
||||
}
|
||||
if err := c.conn.SetDeadline(time.Now().Add(timeout)); err != nil {
|
||||
return fmt.Errorf("smtp: failed to update deadline: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetTLSConnectionState retrieves the TLS connection state of the client's current connection.
|
||||
// Returns an error if the connection is not using TLS or if the connection is not established.
|
||||
func (c *Client) GetTLSConnectionState() (*tls.ConnectionState, error) {
|
||||
c.mutex.RLock()
|
||||
defer c.mutex.RUnlock()
|
||||
|
||||
if !c.isConnected {
|
||||
return nil, ErrNoConnection
|
||||
}
|
||||
if !c.tls {
|
||||
return nil, ErrNonTLSConnection
|
||||
}
|
||||
if conn, ok := c.conn.(*tls.Conn); ok {
|
||||
cstate := conn.ConnectionState()
|
||||
return &cstate, nil
|
||||
}
|
||||
return nil, errors.New("unable to retrieve TLS connection state")
|
||||
}
|
||||
|
||||
// debugLog checks if the debug flag is set and if so logs the provided message to
|
||||
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2010 The Go Authors. All rights reserved.
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2022-2023 The go-mail Authors
|
||||
// SPDX-FileCopyrightText: Copyright (c) The go-mail Authors
|
||||
//
|
||||
// Original net/smtp code from the Go stdlib by the Go Authors.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
@@ -11,9 +11,6 @@
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-3-Clause AND MIT
|
||||
|
||||
//go:build go1.18
|
||||
// +build go1.18
|
||||
|
||||
package smtp
|
||||
|
||||
import "strings"
|
||||
@@ -25,6 +22,9 @@ func (c *Client) ehlo() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
ext := make(map[string]string)
|
||||
extList := strings.Split(msg, "\n")
|
||||
if len(extList) > 1 {
|
||||
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2010 The Go Authors. All rights reserved.
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2022-2023 The go-mail Authors
|
||||
//
|
||||
// Original net/smtp code from the Go stdlib by the Go Authors.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// LICENSE file that can be found in this directory.
|
||||
//
|
||||
// go-mail specific modifications by the go-mail Authors.
|
||||
// Licensed under the MIT License.
|
||||
// See [PROJECT ROOT]/LICENSES directory for more information.
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-3-Clause AND MIT
|
||||
|
||||
//go:build !go1.18
|
||||
// +build !go1.18
|
||||
|
||||
package smtp
|
||||
|
||||
import "strings"
|
||||
|
||||
// ehlo sends the EHLO (extended hello) greeting to the server. It
|
||||
// should be the preferred greeting for servers that support it.
|
||||
//
|
||||
// Backport of: https://github.com/golang/go/commit/4d8db00641cc9ff4f44de7df9b8c4f4a4f9416ee#diff-4f6f6bdb9891d4dd271f9f31430420a2e44018fe4ee539576faf458bebb3cee4
|
||||
// to guarantee backwards compatibility with Go 1.16/1.17:w
|
||||
func (c *Client) ehlo() error {
|
||||
_, msg, err := c.cmd(250, "EHLO %s", c.localName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ext := make(map[string]string)
|
||||
extList := strings.Split(msg, "\n")
|
||||
if len(extList) > 1 {
|
||||
extList = extList[1:]
|
||||
for _, line := range extList {
|
||||
args := strings.SplitN(line, " ", 2)
|
||||
if len(args) > 1 {
|
||||
ext[args[0]] = args[1]
|
||||
} else {
|
||||
ext[args[0]] = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
if mechs, ok := ext["AUTH"]; ok {
|
||||
c.auth = strings.Split(mechs, " ")
|
||||
}
|
||||
c.ext = ext
|
||||
return err
|
||||
}
|
||||
-6
@@ -1,6 +0,0 @@
|
||||
# SPDX-FileCopyrightText: 2022-2023 The go-mail Authors
|
||||
#
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
sonar.projectKey=go-mail
|
||||
sonar.go.coverage.reportPaths=cov.out
|
||||
+13
-6
@@ -1,28 +1,35 @@
|
||||
// SPDX-FileCopyrightText: 2022-2023 The go-mail Authors
|
||||
// SPDX-FileCopyrightText: The go-mail Authors
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package mail
|
||||
|
||||
// TLSPolicy type describes a int alias for the different TLS policies we allow
|
||||
// TLSPolicy is a type wrapper for an int type and describes the different TLS policies we allow.
|
||||
type TLSPolicy int
|
||||
|
||||
const (
|
||||
// TLSMandatory requires that the connection to the server is
|
||||
// encrypting using STARTTLS. If the server does not support STARTTLS
|
||||
// the connection will be terminated with an error
|
||||
// the connection will be terminated with an error.
|
||||
TLSMandatory TLSPolicy = iota
|
||||
|
||||
// TLSOpportunistic tries to establish an encrypted connection via the
|
||||
// STARTTLS protocol. If the server does not support this, it will fall
|
||||
// back to non-encrypted plaintext transmission
|
||||
// back to non-encrypted plaintext transmission.
|
||||
TLSOpportunistic
|
||||
|
||||
// NoTLS forces the transaction to be not encrypted
|
||||
// NoTLS forces the transaction to be not encrypted.
|
||||
NoTLS
|
||||
)
|
||||
|
||||
// String is a standard method to convert a TLSPolicy into a printable format
|
||||
// String satisfies the fmt.Stringer interface for the TLSPolicy type.
|
||||
//
|
||||
// This function returns a string representation of the TLSPolicy. It matches the policy
|
||||
// value to predefined constants and returns the corresponding string. If the policy does
|
||||
// not match any known values, it returns "UnknownPolicy".
|
||||
//
|
||||
// Returns:
|
||||
// - A string representing the TLSPolicy.
|
||||
func (p TLSPolicy) String() string {
|
||||
switch p {
|
||||
case TLSMandatory:
|
||||
|
||||
+8
@@ -12,6 +12,8 @@ import (
|
||||
|
||||
// XOF defines the interface to hash functions that
|
||||
// support arbitrary-length output.
|
||||
//
|
||||
// New callers should prefer the standard library [hash.XOF].
|
||||
type XOF interface {
|
||||
// Write absorbs more data into the hash's state. It panics if called
|
||||
// after Read.
|
||||
@@ -47,6 +49,8 @@ const maxOutputLength = (1 << 32) * 64
|
||||
//
|
||||
// A non-nil key turns the hash into a MAC. The key must between
|
||||
// zero and 32 bytes long.
|
||||
//
|
||||
// The result can be safely interface-upgraded to [hash.XOF].
|
||||
func NewXOF(size uint32, key []byte) (XOF, error) {
|
||||
if len(key) > Size {
|
||||
return nil, errKeySize
|
||||
@@ -93,6 +97,10 @@ func (x *xof) Clone() XOF {
|
||||
return &clone
|
||||
}
|
||||
|
||||
func (x *xof) BlockSize() int {
|
||||
return x.d.BlockSize()
|
||||
}
|
||||
|
||||
func (x *xof) Reset() {
|
||||
x.cfg[0] = byte(Size)
|
||||
binary.LittleEndian.PutUint32(x.cfg[4:], uint32(Size)) // leaf length
|
||||
|
||||
+18
-12
@@ -22,7 +22,10 @@
|
||||
// as shorthands for vMAJOR.0.0 and vMAJOR.MINOR.0.
|
||||
package semver
|
||||
|
||||
import "sort"
|
||||
import (
|
||||
"slices"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// parsed returns the parsed form of a semantic version string.
|
||||
type parsed struct {
|
||||
@@ -154,19 +157,22 @@ func Max(v, w string) string {
|
||||
// ByVersion implements [sort.Interface] for sorting semantic version strings.
|
||||
type ByVersion []string
|
||||
|
||||
func (vs ByVersion) Len() int { return len(vs) }
|
||||
func (vs ByVersion) Swap(i, j int) { vs[i], vs[j] = vs[j], vs[i] }
|
||||
func (vs ByVersion) Less(i, j int) bool {
|
||||
cmp := Compare(vs[i], vs[j])
|
||||
if cmp != 0 {
|
||||
return cmp < 0
|
||||
}
|
||||
return vs[i] < vs[j]
|
||||
func (vs ByVersion) Len() int { return len(vs) }
|
||||
func (vs ByVersion) Swap(i, j int) { vs[i], vs[j] = vs[j], vs[i] }
|
||||
func (vs ByVersion) Less(i, j int) bool { return compareVersion(vs[i], vs[j]) < 0 }
|
||||
|
||||
// Sort sorts a list of semantic version strings using [Compare] and falls back
|
||||
// to use [strings.Compare] if both versions are considered equal.
|
||||
func Sort(list []string) {
|
||||
slices.SortFunc(list, compareVersion)
|
||||
}
|
||||
|
||||
// Sort sorts a list of semantic version strings using [ByVersion].
|
||||
func Sort(list []string) {
|
||||
sort.Sort(ByVersion(list))
|
||||
func compareVersion(a, b string) int {
|
||||
cmp := Compare(a, b)
|
||||
if cmp != 0 {
|
||||
return cmp
|
||||
}
|
||||
return strings.Compare(a, b)
|
||||
}
|
||||
|
||||
func parse(v string) (p parsed, ok bool) {
|
||||
|
||||
+8
-8
@@ -39,7 +39,7 @@ const (
|
||||
FrameContinuation FrameType = 0x9
|
||||
)
|
||||
|
||||
var frameName = map[FrameType]string{
|
||||
var frameNames = [...]string{
|
||||
FrameData: "DATA",
|
||||
FrameHeaders: "HEADERS",
|
||||
FramePriority: "PRIORITY",
|
||||
@@ -53,10 +53,10 @@ var frameName = map[FrameType]string{
|
||||
}
|
||||
|
||||
func (t FrameType) String() string {
|
||||
if s, ok := frameName[t]; ok {
|
||||
return s
|
||||
if int(t) < len(frameNames) {
|
||||
return frameNames[t]
|
||||
}
|
||||
return fmt.Sprintf("UNKNOWN_FRAME_TYPE_%d", uint8(t))
|
||||
return fmt.Sprintf("UNKNOWN_FRAME_TYPE_%d", t)
|
||||
}
|
||||
|
||||
// Flags is a bitmask of HTTP/2 flags.
|
||||
@@ -124,7 +124,7 @@ var flagName = map[FrameType]map[Flags]string{
|
||||
// might be 0).
|
||||
type frameParser func(fc *frameCache, fh FrameHeader, countError func(string), payload []byte) (Frame, error)
|
||||
|
||||
var frameParsers = map[FrameType]frameParser{
|
||||
var frameParsers = [...]frameParser{
|
||||
FrameData: parseDataFrame,
|
||||
FrameHeaders: parseHeadersFrame,
|
||||
FramePriority: parsePriorityFrame,
|
||||
@@ -138,8 +138,8 @@ var frameParsers = map[FrameType]frameParser{
|
||||
}
|
||||
|
||||
func typeFrameParser(t FrameType) frameParser {
|
||||
if f := frameParsers[t]; f != nil {
|
||||
return f
|
||||
if int(t) < len(frameParsers) {
|
||||
return frameParsers[t]
|
||||
}
|
||||
return parseUnknownFrame
|
||||
}
|
||||
@@ -509,7 +509,7 @@ func (fr *Framer) ReadFrame() (Frame, error) {
|
||||
}
|
||||
if fh.Length > fr.maxReadSize {
|
||||
if fh == invalidHTTP1LookingFrameHeader() {
|
||||
return nil, fmt.Errorf("http2: failed reading the frame payload: %w, note that the frame header looked like an HTTP/1.1 header", err)
|
||||
return nil, fmt.Errorf("http2: failed reading the frame payload: %w, note that the frame header looked like an HTTP/1.1 header", ErrFrameTooLarge)
|
||||
}
|
||||
return nil, ErrFrameTooLarge
|
||||
}
|
||||
|
||||
-2
@@ -11,8 +11,6 @@
|
||||
// requires Go 1.6 or later)
|
||||
//
|
||||
// See https://http2.github.io/ for more information on HTTP/2.
|
||||
//
|
||||
// See https://http2.golang.org/ for a test server running this code.
|
||||
package http2 // import "golang.org/x/net/http2"
|
||||
|
||||
import (
|
||||
|
||||
+17
-3
@@ -61,12 +61,14 @@ func (g *Group) Wait() error {
|
||||
}
|
||||
|
||||
// Go calls the given function in a new goroutine.
|
||||
//
|
||||
// The first call to Go must happen before a Wait.
|
||||
// It blocks until the new goroutine can be added without the number of
|
||||
// active goroutines in the group exceeding the configured limit.
|
||||
// goroutines in the group exceeding the configured limit.
|
||||
//
|
||||
// The first call to return a non-nil error cancels the group's context, if the
|
||||
// group was created by calling WithContext. The error will be returned by Wait.
|
||||
// The first goroutine in the group that returns a non-nil error will
|
||||
// cancel the associated Context, if any. The error will be returned
|
||||
// by Wait.
|
||||
func (g *Group) Go(f func() error) {
|
||||
if g.sem != nil {
|
||||
g.sem <- token{}
|
||||
@@ -76,6 +78,18 @@ func (g *Group) Go(f func() error) {
|
||||
go func() {
|
||||
defer g.done()
|
||||
|
||||
// It is tempting to propagate panics from f()
|
||||
// up to the goroutine that calls Wait, but
|
||||
// it creates more problems than it solves:
|
||||
// - it delays panics arbitrarily,
|
||||
// making bugs harder to detect;
|
||||
// - it turns f's panic stack into a mere value,
|
||||
// hiding it from crash-monitoring tools;
|
||||
// - it risks deadlocks that hide the panic entirely,
|
||||
// if f's panic leaves the program in a state
|
||||
// that prevents the Wait call from being reached.
|
||||
// See #53757, #74275, #74304, #74306.
|
||||
|
||||
if err := f(); err != nil {
|
||||
g.errOnce.Do(func() {
|
||||
g.err = err
|
||||
|
||||
+11
@@ -232,6 +232,17 @@ var RISCV64 struct {
|
||||
HasZba bool // Address generation instructions extension
|
||||
HasZbb bool // Basic bit-manipulation extension
|
||||
HasZbs bool // Single-bit instructions extension
|
||||
HasZvbb bool // Vector Basic Bit-manipulation
|
||||
HasZvbc bool // Vector Carryless Multiplication
|
||||
HasZvkb bool // Vector Cryptography Bit-manipulation
|
||||
HasZvkt bool // Vector Data-Independent Execution Latency
|
||||
HasZvkg bool // Vector GCM/GMAC
|
||||
HasZvkn bool // NIST Algorithm Suite (AES/SHA256/SHA512)
|
||||
HasZvknc bool // NIST Algorithm Suite with carryless multiply
|
||||
HasZvkng bool // NIST Algorithm Suite with GCM
|
||||
HasZvks bool // ShangMi Algorithm Suite
|
||||
HasZvksc bool // ShangMi Algorithm Suite with carryless multiplication
|
||||
HasZvksg bool // ShangMi Algorithm Suite with GCM
|
||||
_ CacheLinePad
|
||||
}
|
||||
|
||||
|
||||
+23
@@ -58,6 +58,15 @@ const (
|
||||
riscv_HWPROBE_EXT_ZBA = 0x8
|
||||
riscv_HWPROBE_EXT_ZBB = 0x10
|
||||
riscv_HWPROBE_EXT_ZBS = 0x20
|
||||
riscv_HWPROBE_EXT_ZVBB = 0x20000
|
||||
riscv_HWPROBE_EXT_ZVBC = 0x40000
|
||||
riscv_HWPROBE_EXT_ZVKB = 0x80000
|
||||
riscv_HWPROBE_EXT_ZVKG = 0x100000
|
||||
riscv_HWPROBE_EXT_ZVKNED = 0x200000
|
||||
riscv_HWPROBE_EXT_ZVKNHB = 0x800000
|
||||
riscv_HWPROBE_EXT_ZVKSED = 0x1000000
|
||||
riscv_HWPROBE_EXT_ZVKSH = 0x2000000
|
||||
riscv_HWPROBE_EXT_ZVKT = 0x4000000
|
||||
riscv_HWPROBE_KEY_CPUPERF_0 = 0x5
|
||||
riscv_HWPROBE_MISALIGNED_FAST = 0x3
|
||||
riscv_HWPROBE_MISALIGNED_MASK = 0x7
|
||||
@@ -99,6 +108,20 @@ func doinit() {
|
||||
RISCV64.HasZba = isSet(v, riscv_HWPROBE_EXT_ZBA)
|
||||
RISCV64.HasZbb = isSet(v, riscv_HWPROBE_EXT_ZBB)
|
||||
RISCV64.HasZbs = isSet(v, riscv_HWPROBE_EXT_ZBS)
|
||||
RISCV64.HasZvbb = isSet(v, riscv_HWPROBE_EXT_ZVBB)
|
||||
RISCV64.HasZvbc = isSet(v, riscv_HWPROBE_EXT_ZVBC)
|
||||
RISCV64.HasZvkb = isSet(v, riscv_HWPROBE_EXT_ZVKB)
|
||||
RISCV64.HasZvkg = isSet(v, riscv_HWPROBE_EXT_ZVKG)
|
||||
RISCV64.HasZvkt = isSet(v, riscv_HWPROBE_EXT_ZVKT)
|
||||
// Cryptography shorthand extensions
|
||||
RISCV64.HasZvkn = isSet(v, riscv_HWPROBE_EXT_ZVKNED) &&
|
||||
isSet(v, riscv_HWPROBE_EXT_ZVKNHB) && RISCV64.HasZvkb && RISCV64.HasZvkt
|
||||
RISCV64.HasZvknc = RISCV64.HasZvkn && RISCV64.HasZvbc
|
||||
RISCV64.HasZvkng = RISCV64.HasZvkn && RISCV64.HasZvkg
|
||||
RISCV64.HasZvks = isSet(v, riscv_HWPROBE_EXT_ZVKSED) &&
|
||||
isSet(v, riscv_HWPROBE_EXT_ZVKSH) && RISCV64.HasZvkb && RISCV64.HasZvkt
|
||||
RISCV64.HasZvksc = RISCV64.HasZvks && RISCV64.HasZvbc
|
||||
RISCV64.HasZvksg = RISCV64.HasZvks && RISCV64.HasZvkg
|
||||
}
|
||||
if pairs[1].key != -1 {
|
||||
v := pairs[1].value & riscv_HWPROBE_MISALIGNED_MASK
|
||||
|
||||
+12
@@ -16,5 +16,17 @@ func initOptions() {
|
||||
{Name: "zba", Feature: &RISCV64.HasZba},
|
||||
{Name: "zbb", Feature: &RISCV64.HasZbb},
|
||||
{Name: "zbs", Feature: &RISCV64.HasZbs},
|
||||
// RISC-V Cryptography Extensions
|
||||
{Name: "zvbb", Feature: &RISCV64.HasZvbb},
|
||||
{Name: "zvbc", Feature: &RISCV64.HasZvbc},
|
||||
{Name: "zvkb", Feature: &RISCV64.HasZvkb},
|
||||
{Name: "zvkg", Feature: &RISCV64.HasZvkg},
|
||||
{Name: "zvkt", Feature: &RISCV64.HasZvkt},
|
||||
{Name: "zvkn", Feature: &RISCV64.HasZvkn},
|
||||
{Name: "zvknc", Feature: &RISCV64.HasZvknc},
|
||||
{Name: "zvkng", Feature: &RISCV64.HasZvkng},
|
||||
{Name: "zvks", Feature: &RISCV64.HasZvks},
|
||||
{Name: "zvksc", Feature: &RISCV64.HasZvksc},
|
||||
{Name: "zvksg", Feature: &RISCV64.HasZvksg},
|
||||
}
|
||||
}
|
||||
|
||||
+3
@@ -349,6 +349,9 @@ struct ltchars {
|
||||
#define _HIDIOCGRAWPHYS HIDIOCGRAWPHYS(_HIDIOCGRAWPHYS_LEN)
|
||||
#define _HIDIOCGRAWUNIQ HIDIOCGRAWUNIQ(_HIDIOCGRAWUNIQ_LEN)
|
||||
|
||||
// Renamed in v6.16, commit c6d732c38f93 ("net: ethtool: remove duplicate defines for family info")
|
||||
#define ETHTOOL_FAMILY_NAME ETHTOOL_GENL_NAME
|
||||
#define ETHTOOL_FAMILY_VERSION ETHTOOL_GENL_VERSION
|
||||
'
|
||||
|
||||
includes_NetBSD='
|
||||
|
||||
+1
-55
@@ -602,14 +602,9 @@ func Connectx(fd int, srcIf uint32, srcAddr, dstAddr Sockaddr, associd SaeAssocI
|
||||
return
|
||||
}
|
||||
|
||||
// sys connectx(fd int, endpoints *SaEndpoints, associd SaeAssocID, flags uint32, iov []Iovec, n *uintptr, connid *SaeConnID) (err error)
|
||||
const minIovec = 8
|
||||
|
||||
func Readv(fd int, iovs [][]byte) (n int, err error) {
|
||||
if !darwinKernelVersionMin(11, 0, 0) {
|
||||
return 0, ENOSYS
|
||||
}
|
||||
|
||||
iovecs := make([]Iovec, 0, minIovec)
|
||||
iovecs = appendBytes(iovecs, iovs)
|
||||
n, err = readv(fd, iovecs)
|
||||
@@ -618,9 +613,6 @@ func Readv(fd int, iovs [][]byte) (n int, err error) {
|
||||
}
|
||||
|
||||
func Preadv(fd int, iovs [][]byte, offset int64) (n int, err error) {
|
||||
if !darwinKernelVersionMin(11, 0, 0) {
|
||||
return 0, ENOSYS
|
||||
}
|
||||
iovecs := make([]Iovec, 0, minIovec)
|
||||
iovecs = appendBytes(iovecs, iovs)
|
||||
n, err = preadv(fd, iovecs, offset)
|
||||
@@ -629,10 +621,6 @@ func Preadv(fd int, iovs [][]byte, offset int64) (n int, err error) {
|
||||
}
|
||||
|
||||
func Writev(fd int, iovs [][]byte) (n int, err error) {
|
||||
if !darwinKernelVersionMin(11, 0, 0) {
|
||||
return 0, ENOSYS
|
||||
}
|
||||
|
||||
iovecs := make([]Iovec, 0, minIovec)
|
||||
iovecs = appendBytes(iovecs, iovs)
|
||||
if raceenabled {
|
||||
@@ -644,10 +632,6 @@ func Writev(fd int, iovs [][]byte) (n int, err error) {
|
||||
}
|
||||
|
||||
func Pwritev(fd int, iovs [][]byte, offset int64) (n int, err error) {
|
||||
if !darwinKernelVersionMin(11, 0, 0) {
|
||||
return 0, ENOSYS
|
||||
}
|
||||
|
||||
iovecs := make([]Iovec, 0, minIovec)
|
||||
iovecs = appendBytes(iovecs, iovs)
|
||||
if raceenabled {
|
||||
@@ -707,45 +691,7 @@ func readvRacedetect(iovecs []Iovec, n int, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
func darwinMajorMinPatch() (maj, min, patch int, err error) {
|
||||
var un Utsname
|
||||
err = Uname(&un)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var mmp [3]int
|
||||
c := 0
|
||||
Loop:
|
||||
for _, b := range un.Release[:] {
|
||||
switch {
|
||||
case b >= '0' && b <= '9':
|
||||
mmp[c] = 10*mmp[c] + int(b-'0')
|
||||
case b == '.':
|
||||
c++
|
||||
if c > 2 {
|
||||
return 0, 0, 0, ENOTSUP
|
||||
}
|
||||
case b == 0:
|
||||
break Loop
|
||||
default:
|
||||
return 0, 0, 0, ENOTSUP
|
||||
}
|
||||
}
|
||||
if c != 2 {
|
||||
return 0, 0, 0, ENOTSUP
|
||||
}
|
||||
return mmp[0], mmp[1], mmp[2], nil
|
||||
}
|
||||
|
||||
func darwinKernelVersionMin(maj, min, patch int) bool {
|
||||
actualMaj, actualMin, actualPatch, err := darwinMajorMinPatch()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return actualMaj > maj || actualMaj == maj && (actualMin > min || actualMin == min && actualPatch >= patch)
|
||||
}
|
||||
|
||||
//sys connectx(fd int, endpoints *SaEndpoints, associd SaeAssocID, flags uint32, iov []Iovec, n *uintptr, connid *SaeConnID) (err error)
|
||||
//sys sendfile(infd int, outfd int, offset int64, len *int64, hdtr unsafe.Pointer, flags int) (err error)
|
||||
|
||||
//sys shmat(id int, addr uintptr, flag int) (ret uintptr, err error)
|
||||
|
||||
+47
-16
@@ -319,6 +319,7 @@ const (
|
||||
AUDIT_INTEGRITY_POLICY_RULE = 0x70f
|
||||
AUDIT_INTEGRITY_RULE = 0x70d
|
||||
AUDIT_INTEGRITY_STATUS = 0x70a
|
||||
AUDIT_INTEGRITY_USERSPACE = 0x710
|
||||
AUDIT_IPC = 0x517
|
||||
AUDIT_IPC_SET_PERM = 0x51f
|
||||
AUDIT_IPE_ACCESS = 0x58c
|
||||
@@ -327,6 +328,8 @@ const (
|
||||
AUDIT_KERNEL = 0x7d0
|
||||
AUDIT_KERNEL_OTHER = 0x524
|
||||
AUDIT_KERN_MODULE = 0x532
|
||||
AUDIT_LANDLOCK_ACCESS = 0x58f
|
||||
AUDIT_LANDLOCK_DOMAIN = 0x590
|
||||
AUDIT_LAST_FEATURE = 0x1
|
||||
AUDIT_LAST_KERN_ANOM_MSG = 0x707
|
||||
AUDIT_LAST_USER_MSG = 0x4af
|
||||
@@ -491,6 +494,7 @@ const (
|
||||
BPF_F_BEFORE = 0x8
|
||||
BPF_F_ID = 0x20
|
||||
BPF_F_NETFILTER_IP_DEFRAG = 0x1
|
||||
BPF_F_PREORDER = 0x40
|
||||
BPF_F_QUERY_EFFECTIVE = 0x1
|
||||
BPF_F_REDIRECT_FLAGS = 0x19
|
||||
BPF_F_REPLACE = 0x4
|
||||
@@ -527,6 +531,7 @@ const (
|
||||
BPF_LDX = 0x1
|
||||
BPF_LEN = 0x80
|
||||
BPF_LL_OFF = -0x200000
|
||||
BPF_LOAD_ACQ = 0x100
|
||||
BPF_LSH = 0x60
|
||||
BPF_MAJOR_VERSION = 0x1
|
||||
BPF_MAXINSNS = 0x1000
|
||||
@@ -554,6 +559,7 @@ const (
|
||||
BPF_RET = 0x6
|
||||
BPF_RSH = 0x70
|
||||
BPF_ST = 0x2
|
||||
BPF_STORE_REL = 0x110
|
||||
BPF_STX = 0x3
|
||||
BPF_SUB = 0x10
|
||||
BPF_TAG_SIZE = 0x8
|
||||
@@ -843,9 +849,9 @@ const (
|
||||
DM_UUID_FLAG = 0x4000
|
||||
DM_UUID_LEN = 0x81
|
||||
DM_VERSION = 0xc138fd00
|
||||
DM_VERSION_EXTRA = "-ioctl (2023-03-01)"
|
||||
DM_VERSION_EXTRA = "-ioctl (2025-04-28)"
|
||||
DM_VERSION_MAJOR = 0x4
|
||||
DM_VERSION_MINOR = 0x30
|
||||
DM_VERSION_MINOR = 0x32
|
||||
DM_VERSION_PATCHLEVEL = 0x0
|
||||
DT_BLK = 0x6
|
||||
DT_CHR = 0x2
|
||||
@@ -936,11 +942,10 @@ const (
|
||||
EPOLL_CTL_MOD = 0x3
|
||||
EPOLL_IOC_TYPE = 0x8a
|
||||
EROFS_SUPER_MAGIC_V1 = 0xe0f5e1e2
|
||||
ESP_V4_FLOW = 0xa
|
||||
ESP_V6_FLOW = 0xc
|
||||
ETHER_FLOW = 0x12
|
||||
ETHTOOL_BUSINFO_LEN = 0x20
|
||||
ETHTOOL_EROMVERS_LEN = 0x20
|
||||
ETHTOOL_FAMILY_NAME = "ethtool"
|
||||
ETHTOOL_FAMILY_VERSION = 0x1
|
||||
ETHTOOL_FEC_AUTO = 0x2
|
||||
ETHTOOL_FEC_BASER = 0x10
|
||||
ETHTOOL_FEC_LLRS = 0x20
|
||||
@@ -1203,13 +1208,18 @@ const (
|
||||
FAN_DENY = 0x2
|
||||
FAN_ENABLE_AUDIT = 0x40
|
||||
FAN_EPIDFD = -0x2
|
||||
FAN_ERRNO_BITS = 0x8
|
||||
FAN_ERRNO_MASK = 0xff
|
||||
FAN_ERRNO_SHIFT = 0x18
|
||||
FAN_EVENT_INFO_TYPE_DFID = 0x3
|
||||
FAN_EVENT_INFO_TYPE_DFID_NAME = 0x2
|
||||
FAN_EVENT_INFO_TYPE_ERROR = 0x5
|
||||
FAN_EVENT_INFO_TYPE_FID = 0x1
|
||||
FAN_EVENT_INFO_TYPE_MNT = 0x7
|
||||
FAN_EVENT_INFO_TYPE_NEW_DFID_NAME = 0xc
|
||||
FAN_EVENT_INFO_TYPE_OLD_DFID_NAME = 0xa
|
||||
FAN_EVENT_INFO_TYPE_PIDFD = 0x4
|
||||
FAN_EVENT_INFO_TYPE_RANGE = 0x6
|
||||
FAN_EVENT_METADATA_LEN = 0x18
|
||||
FAN_EVENT_ON_CHILD = 0x8000000
|
||||
FAN_FS_ERROR = 0x8000
|
||||
@@ -1224,9 +1234,12 @@ const (
|
||||
FAN_MARK_IGNORED_SURV_MODIFY = 0x40
|
||||
FAN_MARK_IGNORE_SURV = 0x440
|
||||
FAN_MARK_INODE = 0x0
|
||||
FAN_MARK_MNTNS = 0x110
|
||||
FAN_MARK_MOUNT = 0x10
|
||||
FAN_MARK_ONLYDIR = 0x8
|
||||
FAN_MARK_REMOVE = 0x2
|
||||
FAN_MNT_ATTACH = 0x1000000
|
||||
FAN_MNT_DETACH = 0x2000000
|
||||
FAN_MODIFY = 0x2
|
||||
FAN_MOVE = 0xc0
|
||||
FAN_MOVED_FROM = 0x40
|
||||
@@ -1240,6 +1253,7 @@ const (
|
||||
FAN_OPEN_EXEC = 0x1000
|
||||
FAN_OPEN_EXEC_PERM = 0x40000
|
||||
FAN_OPEN_PERM = 0x10000
|
||||
FAN_PRE_ACCESS = 0x100000
|
||||
FAN_Q_OVERFLOW = 0x4000
|
||||
FAN_RENAME = 0x10000000
|
||||
FAN_REPORT_DFID_NAME = 0xc00
|
||||
@@ -1247,6 +1261,7 @@ const (
|
||||
FAN_REPORT_DIR_FID = 0x400
|
||||
FAN_REPORT_FD_ERROR = 0x2000
|
||||
FAN_REPORT_FID = 0x200
|
||||
FAN_REPORT_MNT = 0x4000
|
||||
FAN_REPORT_NAME = 0x800
|
||||
FAN_REPORT_PIDFD = 0x80
|
||||
FAN_REPORT_TARGET_FID = 0x1000
|
||||
@@ -1266,6 +1281,7 @@ const (
|
||||
FIB_RULE_PERMANENT = 0x1
|
||||
FIB_RULE_UNRESOLVED = 0x4
|
||||
FIDEDUPERANGE = 0xc0189436
|
||||
FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED = 0x1
|
||||
FSCRYPT_KEY_DESCRIPTOR_SIZE = 0x8
|
||||
FSCRYPT_KEY_DESC_PREFIX = "fscrypt:"
|
||||
FSCRYPT_KEY_DESC_PREFIX_SIZE = 0x8
|
||||
@@ -1574,7 +1590,6 @@ const (
|
||||
IPV6_DONTFRAG = 0x3e
|
||||
IPV6_DROP_MEMBERSHIP = 0x15
|
||||
IPV6_DSTOPTS = 0x3b
|
||||
IPV6_FLOW = 0x11
|
||||
IPV6_FREEBIND = 0x4e
|
||||
IPV6_HDRINCL = 0x24
|
||||
IPV6_HOPLIMIT = 0x34
|
||||
@@ -1625,7 +1640,6 @@ const (
|
||||
IPV6_TRANSPARENT = 0x4b
|
||||
IPV6_UNICAST_HOPS = 0x10
|
||||
IPV6_UNICAST_IF = 0x4c
|
||||
IPV6_USER_FLOW = 0xe
|
||||
IPV6_V6ONLY = 0x1a
|
||||
IPV6_VERSION = 0x60
|
||||
IPV6_VERSION_MASK = 0xf0
|
||||
@@ -1687,7 +1701,6 @@ const (
|
||||
IP_TTL = 0x2
|
||||
IP_UNBLOCK_SOURCE = 0x25
|
||||
IP_UNICAST_IF = 0x32
|
||||
IP_USER_FLOW = 0xd
|
||||
IP_XFRM_POLICY = 0x11
|
||||
ISOFS_SUPER_MAGIC = 0x9660
|
||||
ISTRIP = 0x20
|
||||
@@ -1809,7 +1822,11 @@ const (
|
||||
LANDLOCK_ACCESS_FS_WRITE_FILE = 0x2
|
||||
LANDLOCK_ACCESS_NET_BIND_TCP = 0x1
|
||||
LANDLOCK_ACCESS_NET_CONNECT_TCP = 0x2
|
||||
LANDLOCK_CREATE_RULESET_ERRATA = 0x2
|
||||
LANDLOCK_CREATE_RULESET_VERSION = 0x1
|
||||
LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON = 0x2
|
||||
LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF = 0x1
|
||||
LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF = 0x4
|
||||
LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET = 0x1
|
||||
LANDLOCK_SCOPE_SIGNAL = 0x2
|
||||
LINUX_REBOOT_CMD_CAD_OFF = 0x0
|
||||
@@ -2485,6 +2502,10 @@ const (
|
||||
PR_FP_EXC_UND = 0x40000
|
||||
PR_FP_MODE_FR = 0x1
|
||||
PR_FP_MODE_FRE = 0x2
|
||||
PR_FUTEX_HASH = 0x4e
|
||||
PR_FUTEX_HASH_GET_IMMUTABLE = 0x3
|
||||
PR_FUTEX_HASH_GET_SLOTS = 0x2
|
||||
PR_FUTEX_HASH_SET_SLOTS = 0x1
|
||||
PR_GET_AUXV = 0x41555856
|
||||
PR_GET_CHILD_SUBREAPER = 0x25
|
||||
PR_GET_DUMPABLE = 0x3
|
||||
@@ -2644,6 +2665,10 @@ const (
|
||||
PR_TAGGED_ADDR_ENABLE = 0x1
|
||||
PR_TASK_PERF_EVENTS_DISABLE = 0x1f
|
||||
PR_TASK_PERF_EVENTS_ENABLE = 0x20
|
||||
PR_TIMER_CREATE_RESTORE_IDS = 0x4d
|
||||
PR_TIMER_CREATE_RESTORE_IDS_GET = 0x2
|
||||
PR_TIMER_CREATE_RESTORE_IDS_OFF = 0x0
|
||||
PR_TIMER_CREATE_RESTORE_IDS_ON = 0x1
|
||||
PR_TIMING_STATISTICAL = 0x0
|
||||
PR_TIMING_TIMESTAMP = 0x1
|
||||
PR_TSC_ENABLE = 0x1
|
||||
@@ -2724,6 +2749,7 @@ const (
|
||||
PTRACE_SETREGSET = 0x4205
|
||||
PTRACE_SETSIGINFO = 0x4203
|
||||
PTRACE_SETSIGMASK = 0x420b
|
||||
PTRACE_SET_SYSCALL_INFO = 0x4212
|
||||
PTRACE_SET_SYSCALL_USER_DISPATCH_CONFIG = 0x4210
|
||||
PTRACE_SINGLESTEP = 0x9
|
||||
PTRACE_SYSCALL = 0x18
|
||||
@@ -2787,7 +2813,7 @@ const (
|
||||
RTAX_UNSPEC = 0x0
|
||||
RTAX_WINDOW = 0x3
|
||||
RTA_ALIGNTO = 0x4
|
||||
RTA_MAX = 0x1e
|
||||
RTA_MAX = 0x1f
|
||||
RTCF_DIRECTSRC = 0x4000000
|
||||
RTCF_DOREDIRECT = 0x1000000
|
||||
RTCF_LOG = 0x2000000
|
||||
@@ -2864,10 +2890,12 @@ const (
|
||||
RTM_DELACTION = 0x31
|
||||
RTM_DELADDR = 0x15
|
||||
RTM_DELADDRLABEL = 0x49
|
||||
RTM_DELANYCAST = 0x3d
|
||||
RTM_DELCHAIN = 0x65
|
||||
RTM_DELLINK = 0x11
|
||||
RTM_DELLINKPROP = 0x6d
|
||||
RTM_DELMDB = 0x55
|
||||
RTM_DELMULTICAST = 0x39
|
||||
RTM_DELNEIGH = 0x1d
|
||||
RTM_DELNETCONF = 0x51
|
||||
RTM_DELNEXTHOP = 0x69
|
||||
@@ -2917,11 +2945,13 @@ const (
|
||||
RTM_NEWACTION = 0x30
|
||||
RTM_NEWADDR = 0x14
|
||||
RTM_NEWADDRLABEL = 0x48
|
||||
RTM_NEWANYCAST = 0x3c
|
||||
RTM_NEWCACHEREPORT = 0x60
|
||||
RTM_NEWCHAIN = 0x64
|
||||
RTM_NEWLINK = 0x10
|
||||
RTM_NEWLINKPROP = 0x6c
|
||||
RTM_NEWMDB = 0x54
|
||||
RTM_NEWMULTICAST = 0x38
|
||||
RTM_NEWNDUSEROPT = 0x44
|
||||
RTM_NEWNEIGH = 0x1c
|
||||
RTM_NEWNEIGHTBL = 0x40
|
||||
@@ -2970,6 +3000,7 @@ const (
|
||||
RTPROT_NTK = 0xf
|
||||
RTPROT_OPENR = 0x63
|
||||
RTPROT_OSPF = 0xbc
|
||||
RTPROT_OVN = 0x54
|
||||
RTPROT_RA = 0x9
|
||||
RTPROT_REDIRECT = 0x1
|
||||
RTPROT_RIP = 0xbd
|
||||
@@ -2987,11 +3018,12 @@ const (
|
||||
RUSAGE_THREAD = 0x1
|
||||
RWF_APPEND = 0x10
|
||||
RWF_ATOMIC = 0x40
|
||||
RWF_DONTCACHE = 0x80
|
||||
RWF_DSYNC = 0x2
|
||||
RWF_HIPRI = 0x1
|
||||
RWF_NOAPPEND = 0x20
|
||||
RWF_NOWAIT = 0x8
|
||||
RWF_SUPPORTED = 0x7f
|
||||
RWF_SUPPORTED = 0xff
|
||||
RWF_SYNC = 0x4
|
||||
RWF_WRITE_LIFE_NOT_SET = 0x0
|
||||
SCHED_BATCH = 0x3
|
||||
@@ -3271,6 +3303,7 @@ const (
|
||||
STATX_BTIME = 0x800
|
||||
STATX_CTIME = 0x80
|
||||
STATX_DIOALIGN = 0x2000
|
||||
STATX_DIO_READ_ALIGN = 0x20000
|
||||
STATX_GID = 0x10
|
||||
STATX_INO = 0x100
|
||||
STATX_MNT_ID = 0x1000
|
||||
@@ -3322,7 +3355,7 @@ const (
|
||||
TASKSTATS_GENL_NAME = "TASKSTATS"
|
||||
TASKSTATS_GENL_VERSION = 0x1
|
||||
TASKSTATS_TYPE_MAX = 0x6
|
||||
TASKSTATS_VERSION = 0xe
|
||||
TASKSTATS_VERSION = 0x10
|
||||
TCIFLUSH = 0x0
|
||||
TCIOFF = 0x2
|
||||
TCIOFLUSH = 0x2
|
||||
@@ -3392,8 +3425,6 @@ const (
|
||||
TCP_TX_DELAY = 0x25
|
||||
TCP_ULP = 0x1f
|
||||
TCP_USER_TIMEOUT = 0x12
|
||||
TCP_V4_FLOW = 0x1
|
||||
TCP_V6_FLOW = 0x5
|
||||
TCP_WINDOW_CLAMP = 0xa
|
||||
TCP_ZEROCOPY_RECEIVE = 0x23
|
||||
TFD_TIMER_ABSTIME = 0x1
|
||||
@@ -3503,6 +3534,7 @@ const (
|
||||
TP_STATUS_WRONG_FORMAT = 0x4
|
||||
TRACEFS_MAGIC = 0x74726163
|
||||
TS_COMM_LEN = 0x20
|
||||
UBI_IOCECNFO = 0xc01c6f06
|
||||
UDF_SUPER_MAGIC = 0x15013346
|
||||
UDP_CORK = 0x1
|
||||
UDP_ENCAP = 0x64
|
||||
@@ -3515,8 +3547,6 @@ const (
|
||||
UDP_NO_CHECK6_RX = 0x66
|
||||
UDP_NO_CHECK6_TX = 0x65
|
||||
UDP_SEGMENT = 0x67
|
||||
UDP_V4_FLOW = 0x2
|
||||
UDP_V6_FLOW = 0x6
|
||||
UMOUNT_NOFOLLOW = 0x8
|
||||
USBDEVICE_SUPER_MAGIC = 0x9fa2
|
||||
UTIME_NOW = 0x3fffffff
|
||||
@@ -3559,7 +3589,7 @@ const (
|
||||
WDIOS_TEMPPANIC = 0x4
|
||||
WDIOS_UNKNOWN = -0x1
|
||||
WEXITED = 0x4
|
||||
WGALLOWEDIP_A_MAX = 0x3
|
||||
WGALLOWEDIP_A_MAX = 0x4
|
||||
WGDEVICE_A_MAX = 0x8
|
||||
WGPEER_A_MAX = 0xa
|
||||
WG_CMD_MAX = 0x1
|
||||
@@ -3673,6 +3703,7 @@ const (
|
||||
XDP_SHARED_UMEM = 0x1
|
||||
XDP_STATISTICS = 0x7
|
||||
XDP_TXMD_FLAGS_CHECKSUM = 0x2
|
||||
XDP_TXMD_FLAGS_LAUNCH_TIME = 0x4
|
||||
XDP_TXMD_FLAGS_TIMESTAMP = 0x1
|
||||
XDP_TX_METADATA = 0x2
|
||||
XDP_TX_RING = 0x3
|
||||
|
||||
+3
@@ -68,6 +68,7 @@ const (
|
||||
CS8 = 0x30
|
||||
CSIZE = 0x30
|
||||
CSTOPB = 0x40
|
||||
DM_MPATH_PROBE_PATHS = 0xfd12
|
||||
ECCGETLAYOUT = 0x81484d11
|
||||
ECCGETSTATS = 0x80104d12
|
||||
ECHOCTL = 0x200
|
||||
@@ -360,6 +361,7 @@ const (
|
||||
SO_OOBINLINE = 0xa
|
||||
SO_PASSCRED = 0x10
|
||||
SO_PASSPIDFD = 0x4c
|
||||
SO_PASSRIGHTS = 0x53
|
||||
SO_PASSSEC = 0x22
|
||||
SO_PEEK_OFF = 0x2a
|
||||
SO_PEERCRED = 0x11
|
||||
@@ -372,6 +374,7 @@ const (
|
||||
SO_RCVBUFFORCE = 0x21
|
||||
SO_RCVLOWAT = 0x12
|
||||
SO_RCVMARK = 0x4b
|
||||
SO_RCVPRIORITY = 0x52
|
||||
SO_RCVTIMEO = 0x14
|
||||
SO_RCVTIMEO_NEW = 0x42
|
||||
SO_RCVTIMEO_OLD = 0x14
|
||||
|
||||
+3
@@ -68,6 +68,7 @@ const (
|
||||
CS8 = 0x30
|
||||
CSIZE = 0x30
|
||||
CSTOPB = 0x40
|
||||
DM_MPATH_PROBE_PATHS = 0xfd12
|
||||
ECCGETLAYOUT = 0x81484d11
|
||||
ECCGETSTATS = 0x80104d12
|
||||
ECHOCTL = 0x200
|
||||
@@ -361,6 +362,7 @@ const (
|
||||
SO_OOBINLINE = 0xa
|
||||
SO_PASSCRED = 0x10
|
||||
SO_PASSPIDFD = 0x4c
|
||||
SO_PASSRIGHTS = 0x53
|
||||
SO_PASSSEC = 0x22
|
||||
SO_PEEK_OFF = 0x2a
|
||||
SO_PEERCRED = 0x11
|
||||
@@ -373,6 +375,7 @@ const (
|
||||
SO_RCVBUFFORCE = 0x21
|
||||
SO_RCVLOWAT = 0x12
|
||||
SO_RCVMARK = 0x4b
|
||||
SO_RCVPRIORITY = 0x52
|
||||
SO_RCVTIMEO = 0x14
|
||||
SO_RCVTIMEO_NEW = 0x42
|
||||
SO_RCVTIMEO_OLD = 0x14
|
||||
|
||||
+3
@@ -68,6 +68,7 @@ const (
|
||||
CS8 = 0x30
|
||||
CSIZE = 0x30
|
||||
CSTOPB = 0x40
|
||||
DM_MPATH_PROBE_PATHS = 0xfd12
|
||||
ECCGETLAYOUT = 0x81484d11
|
||||
ECCGETSTATS = 0x80104d12
|
||||
ECHOCTL = 0x200
|
||||
@@ -366,6 +367,7 @@ const (
|
||||
SO_OOBINLINE = 0xa
|
||||
SO_PASSCRED = 0x10
|
||||
SO_PASSPIDFD = 0x4c
|
||||
SO_PASSRIGHTS = 0x53
|
||||
SO_PASSSEC = 0x22
|
||||
SO_PEEK_OFF = 0x2a
|
||||
SO_PEERCRED = 0x11
|
||||
@@ -378,6 +380,7 @@ const (
|
||||
SO_RCVBUFFORCE = 0x21
|
||||
SO_RCVLOWAT = 0x12
|
||||
SO_RCVMARK = 0x4b
|
||||
SO_RCVPRIORITY = 0x52
|
||||
SO_RCVTIMEO = 0x14
|
||||
SO_RCVTIMEO_NEW = 0x42
|
||||
SO_RCVTIMEO_OLD = 0x14
|
||||
|
||||
+3
@@ -68,6 +68,7 @@ const (
|
||||
CS8 = 0x30
|
||||
CSIZE = 0x30
|
||||
CSTOPB = 0x40
|
||||
DM_MPATH_PROBE_PATHS = 0xfd12
|
||||
ECCGETLAYOUT = 0x81484d11
|
||||
ECCGETSTATS = 0x80104d12
|
||||
ECHOCTL = 0x200
|
||||
@@ -359,6 +360,7 @@ const (
|
||||
SO_OOBINLINE = 0xa
|
||||
SO_PASSCRED = 0x10
|
||||
SO_PASSPIDFD = 0x4c
|
||||
SO_PASSRIGHTS = 0x53
|
||||
SO_PASSSEC = 0x22
|
||||
SO_PEEK_OFF = 0x2a
|
||||
SO_PEERCRED = 0x11
|
||||
@@ -371,6 +373,7 @@ const (
|
||||
SO_RCVBUFFORCE = 0x21
|
||||
SO_RCVLOWAT = 0x12
|
||||
SO_RCVMARK = 0x4b
|
||||
SO_RCVPRIORITY = 0x52
|
||||
SO_RCVTIMEO = 0x14
|
||||
SO_RCVTIMEO_NEW = 0x42
|
||||
SO_RCVTIMEO_OLD = 0x14
|
||||
|
||||
+3
@@ -68,6 +68,7 @@ const (
|
||||
CS8 = 0x30
|
||||
CSIZE = 0x30
|
||||
CSTOPB = 0x40
|
||||
DM_MPATH_PROBE_PATHS = 0xfd12
|
||||
ECCGETLAYOUT = 0x81484d11
|
||||
ECCGETSTATS = 0x80104d12
|
||||
ECHOCTL = 0x200
|
||||
@@ -353,6 +354,7 @@ const (
|
||||
SO_OOBINLINE = 0xa
|
||||
SO_PASSCRED = 0x10
|
||||
SO_PASSPIDFD = 0x4c
|
||||
SO_PASSRIGHTS = 0x53
|
||||
SO_PASSSEC = 0x22
|
||||
SO_PEEK_OFF = 0x2a
|
||||
SO_PEERCRED = 0x11
|
||||
@@ -365,6 +367,7 @@ const (
|
||||
SO_RCVBUFFORCE = 0x21
|
||||
SO_RCVLOWAT = 0x12
|
||||
SO_RCVMARK = 0x4b
|
||||
SO_RCVPRIORITY = 0x52
|
||||
SO_RCVTIMEO = 0x14
|
||||
SO_RCVTIMEO_NEW = 0x42
|
||||
SO_RCVTIMEO_OLD = 0x14
|
||||
|
||||
+3
@@ -68,6 +68,7 @@ const (
|
||||
CS8 = 0x30
|
||||
CSIZE = 0x30
|
||||
CSTOPB = 0x40
|
||||
DM_MPATH_PROBE_PATHS = 0x2000fd12
|
||||
ECCGETLAYOUT = 0x41484d11
|
||||
ECCGETSTATS = 0x40104d12
|
||||
ECHOCTL = 0x200
|
||||
@@ -359,6 +360,7 @@ const (
|
||||
SO_OOBINLINE = 0x100
|
||||
SO_PASSCRED = 0x11
|
||||
SO_PASSPIDFD = 0x4c
|
||||
SO_PASSRIGHTS = 0x53
|
||||
SO_PASSSEC = 0x22
|
||||
SO_PEEK_OFF = 0x2a
|
||||
SO_PEERCRED = 0x12
|
||||
@@ -371,6 +373,7 @@ const (
|
||||
SO_RCVBUFFORCE = 0x21
|
||||
SO_RCVLOWAT = 0x1004
|
||||
SO_RCVMARK = 0x4b
|
||||
SO_RCVPRIORITY = 0x52
|
||||
SO_RCVTIMEO = 0x1006
|
||||
SO_RCVTIMEO_NEW = 0x42
|
||||
SO_RCVTIMEO_OLD = 0x1006
|
||||
|
||||
+3
@@ -68,6 +68,7 @@ const (
|
||||
CS8 = 0x30
|
||||
CSIZE = 0x30
|
||||
CSTOPB = 0x40
|
||||
DM_MPATH_PROBE_PATHS = 0x2000fd12
|
||||
ECCGETLAYOUT = 0x41484d11
|
||||
ECCGETSTATS = 0x40104d12
|
||||
ECHOCTL = 0x200
|
||||
@@ -359,6 +360,7 @@ const (
|
||||
SO_OOBINLINE = 0x100
|
||||
SO_PASSCRED = 0x11
|
||||
SO_PASSPIDFD = 0x4c
|
||||
SO_PASSRIGHTS = 0x53
|
||||
SO_PASSSEC = 0x22
|
||||
SO_PEEK_OFF = 0x2a
|
||||
SO_PEERCRED = 0x12
|
||||
@@ -371,6 +373,7 @@ const (
|
||||
SO_RCVBUFFORCE = 0x21
|
||||
SO_RCVLOWAT = 0x1004
|
||||
SO_RCVMARK = 0x4b
|
||||
SO_RCVPRIORITY = 0x52
|
||||
SO_RCVTIMEO = 0x1006
|
||||
SO_RCVTIMEO_NEW = 0x42
|
||||
SO_RCVTIMEO_OLD = 0x1006
|
||||
|
||||
+3
@@ -68,6 +68,7 @@ const (
|
||||
CS8 = 0x30
|
||||
CSIZE = 0x30
|
||||
CSTOPB = 0x40
|
||||
DM_MPATH_PROBE_PATHS = 0x2000fd12
|
||||
ECCGETLAYOUT = 0x41484d11
|
||||
ECCGETSTATS = 0x40104d12
|
||||
ECHOCTL = 0x200
|
||||
@@ -359,6 +360,7 @@ const (
|
||||
SO_OOBINLINE = 0x100
|
||||
SO_PASSCRED = 0x11
|
||||
SO_PASSPIDFD = 0x4c
|
||||
SO_PASSRIGHTS = 0x53
|
||||
SO_PASSSEC = 0x22
|
||||
SO_PEEK_OFF = 0x2a
|
||||
SO_PEERCRED = 0x12
|
||||
@@ -371,6 +373,7 @@ const (
|
||||
SO_RCVBUFFORCE = 0x21
|
||||
SO_RCVLOWAT = 0x1004
|
||||
SO_RCVMARK = 0x4b
|
||||
SO_RCVPRIORITY = 0x52
|
||||
SO_RCVTIMEO = 0x1006
|
||||
SO_RCVTIMEO_NEW = 0x42
|
||||
SO_RCVTIMEO_OLD = 0x1006
|
||||
|
||||
+3
@@ -68,6 +68,7 @@ const (
|
||||
CS8 = 0x30
|
||||
CSIZE = 0x30
|
||||
CSTOPB = 0x40
|
||||
DM_MPATH_PROBE_PATHS = 0x2000fd12
|
||||
ECCGETLAYOUT = 0x41484d11
|
||||
ECCGETSTATS = 0x40104d12
|
||||
ECHOCTL = 0x200
|
||||
@@ -359,6 +360,7 @@ const (
|
||||
SO_OOBINLINE = 0x100
|
||||
SO_PASSCRED = 0x11
|
||||
SO_PASSPIDFD = 0x4c
|
||||
SO_PASSRIGHTS = 0x53
|
||||
SO_PASSSEC = 0x22
|
||||
SO_PEEK_OFF = 0x2a
|
||||
SO_PEERCRED = 0x12
|
||||
@@ -371,6 +373,7 @@ const (
|
||||
SO_RCVBUFFORCE = 0x21
|
||||
SO_RCVLOWAT = 0x1004
|
||||
SO_RCVMARK = 0x4b
|
||||
SO_RCVPRIORITY = 0x52
|
||||
SO_RCVTIMEO = 0x1006
|
||||
SO_RCVTIMEO_NEW = 0x42
|
||||
SO_RCVTIMEO_OLD = 0x1006
|
||||
|
||||
+3
@@ -68,6 +68,7 @@ const (
|
||||
CS8 = 0x300
|
||||
CSIZE = 0x300
|
||||
CSTOPB = 0x400
|
||||
DM_MPATH_PROBE_PATHS = 0x2000fd12
|
||||
ECCGETLAYOUT = 0x41484d11
|
||||
ECCGETSTATS = 0x40104d12
|
||||
ECHOCTL = 0x40
|
||||
@@ -414,6 +415,7 @@ const (
|
||||
SO_OOBINLINE = 0xa
|
||||
SO_PASSCRED = 0x14
|
||||
SO_PASSPIDFD = 0x4c
|
||||
SO_PASSRIGHTS = 0x53
|
||||
SO_PASSSEC = 0x22
|
||||
SO_PEEK_OFF = 0x2a
|
||||
SO_PEERCRED = 0x15
|
||||
@@ -426,6 +428,7 @@ const (
|
||||
SO_RCVBUFFORCE = 0x21
|
||||
SO_RCVLOWAT = 0x10
|
||||
SO_RCVMARK = 0x4b
|
||||
SO_RCVPRIORITY = 0x52
|
||||
SO_RCVTIMEO = 0x12
|
||||
SO_RCVTIMEO_NEW = 0x42
|
||||
SO_RCVTIMEO_OLD = 0x12
|
||||
|
||||
+3
@@ -68,6 +68,7 @@ const (
|
||||
CS8 = 0x300
|
||||
CSIZE = 0x300
|
||||
CSTOPB = 0x400
|
||||
DM_MPATH_PROBE_PATHS = 0x2000fd12
|
||||
ECCGETLAYOUT = 0x41484d11
|
||||
ECCGETSTATS = 0x40104d12
|
||||
ECHOCTL = 0x40
|
||||
@@ -418,6 +419,7 @@ const (
|
||||
SO_OOBINLINE = 0xa
|
||||
SO_PASSCRED = 0x14
|
||||
SO_PASSPIDFD = 0x4c
|
||||
SO_PASSRIGHTS = 0x53
|
||||
SO_PASSSEC = 0x22
|
||||
SO_PEEK_OFF = 0x2a
|
||||
SO_PEERCRED = 0x15
|
||||
@@ -430,6 +432,7 @@ const (
|
||||
SO_RCVBUFFORCE = 0x21
|
||||
SO_RCVLOWAT = 0x10
|
||||
SO_RCVMARK = 0x4b
|
||||
SO_RCVPRIORITY = 0x52
|
||||
SO_RCVTIMEO = 0x12
|
||||
SO_RCVTIMEO_NEW = 0x42
|
||||
SO_RCVTIMEO_OLD = 0x12
|
||||
|
||||
+3
@@ -68,6 +68,7 @@ const (
|
||||
CS8 = 0x300
|
||||
CSIZE = 0x300
|
||||
CSTOPB = 0x400
|
||||
DM_MPATH_PROBE_PATHS = 0x2000fd12
|
||||
ECCGETLAYOUT = 0x41484d11
|
||||
ECCGETSTATS = 0x40104d12
|
||||
ECHOCTL = 0x40
|
||||
@@ -418,6 +419,7 @@ const (
|
||||
SO_OOBINLINE = 0xa
|
||||
SO_PASSCRED = 0x14
|
||||
SO_PASSPIDFD = 0x4c
|
||||
SO_PASSRIGHTS = 0x53
|
||||
SO_PASSSEC = 0x22
|
||||
SO_PEEK_OFF = 0x2a
|
||||
SO_PEERCRED = 0x15
|
||||
@@ -430,6 +432,7 @@ const (
|
||||
SO_RCVBUFFORCE = 0x21
|
||||
SO_RCVLOWAT = 0x10
|
||||
SO_RCVMARK = 0x4b
|
||||
SO_RCVPRIORITY = 0x52
|
||||
SO_RCVTIMEO = 0x12
|
||||
SO_RCVTIMEO_NEW = 0x42
|
||||
SO_RCVTIMEO_OLD = 0x12
|
||||
|
||||
+3
@@ -68,6 +68,7 @@ const (
|
||||
CS8 = 0x30
|
||||
CSIZE = 0x30
|
||||
CSTOPB = 0x40
|
||||
DM_MPATH_PROBE_PATHS = 0xfd12
|
||||
ECCGETLAYOUT = 0x81484d11
|
||||
ECCGETSTATS = 0x80104d12
|
||||
ECHOCTL = 0x200
|
||||
@@ -350,6 +351,7 @@ const (
|
||||
SO_OOBINLINE = 0xa
|
||||
SO_PASSCRED = 0x10
|
||||
SO_PASSPIDFD = 0x4c
|
||||
SO_PASSRIGHTS = 0x53
|
||||
SO_PASSSEC = 0x22
|
||||
SO_PEEK_OFF = 0x2a
|
||||
SO_PEERCRED = 0x11
|
||||
@@ -362,6 +364,7 @@ const (
|
||||
SO_RCVBUFFORCE = 0x21
|
||||
SO_RCVLOWAT = 0x12
|
||||
SO_RCVMARK = 0x4b
|
||||
SO_RCVPRIORITY = 0x52
|
||||
SO_RCVTIMEO = 0x14
|
||||
SO_RCVTIMEO_NEW = 0x42
|
||||
SO_RCVTIMEO_OLD = 0x14
|
||||
|
||||
+3
@@ -68,6 +68,7 @@ const (
|
||||
CS8 = 0x30
|
||||
CSIZE = 0x30
|
||||
CSTOPB = 0x40
|
||||
DM_MPATH_PROBE_PATHS = 0xfd12
|
||||
ECCGETLAYOUT = 0x81484d11
|
||||
ECCGETSTATS = 0x80104d12
|
||||
ECHOCTL = 0x200
|
||||
@@ -422,6 +423,7 @@ const (
|
||||
SO_OOBINLINE = 0xa
|
||||
SO_PASSCRED = 0x10
|
||||
SO_PASSPIDFD = 0x4c
|
||||
SO_PASSRIGHTS = 0x53
|
||||
SO_PASSSEC = 0x22
|
||||
SO_PEEK_OFF = 0x2a
|
||||
SO_PEERCRED = 0x11
|
||||
@@ -434,6 +436,7 @@ const (
|
||||
SO_RCVBUFFORCE = 0x21
|
||||
SO_RCVLOWAT = 0x12
|
||||
SO_RCVMARK = 0x4b
|
||||
SO_RCVPRIORITY = 0x52
|
||||
SO_RCVTIMEO = 0x14
|
||||
SO_RCVTIMEO_NEW = 0x42
|
||||
SO_RCVTIMEO_OLD = 0x14
|
||||
|
||||
+3
@@ -71,6 +71,7 @@ const (
|
||||
CS8 = 0x30
|
||||
CSIZE = 0x30
|
||||
CSTOPB = 0x40
|
||||
DM_MPATH_PROBE_PATHS = 0x2000fd12
|
||||
ECCGETLAYOUT = 0x41484d11
|
||||
ECCGETSTATS = 0x40104d12
|
||||
ECHOCTL = 0x200
|
||||
@@ -461,6 +462,7 @@ const (
|
||||
SO_OOBINLINE = 0x100
|
||||
SO_PASSCRED = 0x2
|
||||
SO_PASSPIDFD = 0x55
|
||||
SO_PASSRIGHTS = 0x5c
|
||||
SO_PASSSEC = 0x1f
|
||||
SO_PEEK_OFF = 0x26
|
||||
SO_PEERCRED = 0x40
|
||||
@@ -473,6 +475,7 @@ const (
|
||||
SO_RCVBUFFORCE = 0x100b
|
||||
SO_RCVLOWAT = 0x800
|
||||
SO_RCVMARK = 0x54
|
||||
SO_RCVPRIORITY = 0x5b
|
||||
SO_RCVTIMEO = 0x2000
|
||||
SO_RCVTIMEO_NEW = 0x44
|
||||
SO_RCVTIMEO_OLD = 0x2000
|
||||
|
||||
+1
@@ -462,4 +462,5 @@ const (
|
||||
SYS_GETXATTRAT = 464
|
||||
SYS_LISTXATTRAT = 465
|
||||
SYS_REMOVEXATTRAT = 466
|
||||
SYS_OPEN_TREE_ATTR = 467
|
||||
)
|
||||
|
||||
+1
@@ -385,4 +385,5 @@ const (
|
||||
SYS_GETXATTRAT = 464
|
||||
SYS_LISTXATTRAT = 465
|
||||
SYS_REMOVEXATTRAT = 466
|
||||
SYS_OPEN_TREE_ATTR = 467
|
||||
)
|
||||
|
||||
+1
@@ -426,4 +426,5 @@ const (
|
||||
SYS_GETXATTRAT = 464
|
||||
SYS_LISTXATTRAT = 465
|
||||
SYS_REMOVEXATTRAT = 466
|
||||
SYS_OPEN_TREE_ATTR = 467
|
||||
)
|
||||
|
||||
+1
@@ -329,4 +329,5 @@ const (
|
||||
SYS_GETXATTRAT = 464
|
||||
SYS_LISTXATTRAT = 465
|
||||
SYS_REMOVEXATTRAT = 466
|
||||
SYS_OPEN_TREE_ATTR = 467
|
||||
)
|
||||
|
||||
+1
@@ -325,4 +325,5 @@ const (
|
||||
SYS_GETXATTRAT = 464
|
||||
SYS_LISTXATTRAT = 465
|
||||
SYS_REMOVEXATTRAT = 466
|
||||
SYS_OPEN_TREE_ATTR = 467
|
||||
)
|
||||
|
||||
+1
@@ -446,4 +446,5 @@ const (
|
||||
SYS_GETXATTRAT = 4464
|
||||
SYS_LISTXATTRAT = 4465
|
||||
SYS_REMOVEXATTRAT = 4466
|
||||
SYS_OPEN_TREE_ATTR = 4467
|
||||
)
|
||||
|
||||
+1
@@ -376,4 +376,5 @@ const (
|
||||
SYS_GETXATTRAT = 5464
|
||||
SYS_LISTXATTRAT = 5465
|
||||
SYS_REMOVEXATTRAT = 5466
|
||||
SYS_OPEN_TREE_ATTR = 5467
|
||||
)
|
||||
|
||||
+1
@@ -376,4 +376,5 @@ const (
|
||||
SYS_GETXATTRAT = 5464
|
||||
SYS_LISTXATTRAT = 5465
|
||||
SYS_REMOVEXATTRAT = 5466
|
||||
SYS_OPEN_TREE_ATTR = 5467
|
||||
)
|
||||
|
||||
+1
@@ -446,4 +446,5 @@ const (
|
||||
SYS_GETXATTRAT = 4464
|
||||
SYS_LISTXATTRAT = 4465
|
||||
SYS_REMOVEXATTRAT = 4466
|
||||
SYS_OPEN_TREE_ATTR = 4467
|
||||
)
|
||||
|
||||
+1
@@ -453,4 +453,5 @@ const (
|
||||
SYS_GETXATTRAT = 464
|
||||
SYS_LISTXATTRAT = 465
|
||||
SYS_REMOVEXATTRAT = 466
|
||||
SYS_OPEN_TREE_ATTR = 467
|
||||
)
|
||||
|
||||
+1
@@ -425,4 +425,5 @@ const (
|
||||
SYS_GETXATTRAT = 464
|
||||
SYS_LISTXATTRAT = 465
|
||||
SYS_REMOVEXATTRAT = 466
|
||||
SYS_OPEN_TREE_ATTR = 467
|
||||
)
|
||||
|
||||
+1
@@ -425,4 +425,5 @@ const (
|
||||
SYS_GETXATTRAT = 464
|
||||
SYS_LISTXATTRAT = 465
|
||||
SYS_REMOVEXATTRAT = 466
|
||||
SYS_OPEN_TREE_ATTR = 467
|
||||
)
|
||||
|
||||
+1
@@ -330,4 +330,5 @@ const (
|
||||
SYS_GETXATTRAT = 464
|
||||
SYS_LISTXATTRAT = 465
|
||||
SYS_REMOVEXATTRAT = 466
|
||||
SYS_OPEN_TREE_ATTR = 467
|
||||
)
|
||||
|
||||
+1
@@ -391,4 +391,5 @@ const (
|
||||
SYS_GETXATTRAT = 464
|
||||
SYS_LISTXATTRAT = 465
|
||||
SYS_REMOVEXATTRAT = 466
|
||||
SYS_OPEN_TREE_ATTR = 467
|
||||
)
|
||||
|
||||
+1
@@ -404,4 +404,5 @@ const (
|
||||
SYS_GETXATTRAT = 464
|
||||
SYS_LISTXATTRAT = 465
|
||||
SYS_REMOVEXATTRAT = 466
|
||||
SYS_OPEN_TREE_ATTR = 467
|
||||
)
|
||||
|
||||
+127
-12
@@ -114,8 +114,10 @@ type Statx_t struct {
|
||||
Atomic_write_unit_min uint32
|
||||
Atomic_write_unit_max uint32
|
||||
Atomic_write_segments_max uint32
|
||||
Dio_read_offset_align uint32
|
||||
Atomic_write_unit_max_opt uint32
|
||||
_ [1]uint32
|
||||
_ [9]uint64
|
||||
_ [8]uint64
|
||||
}
|
||||
|
||||
type Fsid struct {
|
||||
@@ -199,7 +201,8 @@ type FscryptAddKeyArg struct {
|
||||
Key_spec FscryptKeySpecifier
|
||||
Raw_size uint32
|
||||
Key_id uint32
|
||||
_ [8]uint32
|
||||
Flags uint32
|
||||
_ [7]uint32
|
||||
}
|
||||
|
||||
type FscryptRemoveKeyArg struct {
|
||||
@@ -2226,8 +2229,11 @@ const (
|
||||
NFT_PAYLOAD_LL_HEADER = 0x0
|
||||
NFT_PAYLOAD_NETWORK_HEADER = 0x1
|
||||
NFT_PAYLOAD_TRANSPORT_HEADER = 0x2
|
||||
NFT_PAYLOAD_INNER_HEADER = 0x3
|
||||
NFT_PAYLOAD_TUN_HEADER = 0x4
|
||||
NFT_PAYLOAD_CSUM_NONE = 0x0
|
||||
NFT_PAYLOAD_CSUM_INET = 0x1
|
||||
NFT_PAYLOAD_CSUM_SCTP = 0x2
|
||||
NFT_PAYLOAD_L4CSUM_PSEUDOHDR = 0x1
|
||||
NFTA_PAYLOAD_UNSPEC = 0x0
|
||||
NFTA_PAYLOAD_DREG = 0x1
|
||||
@@ -2314,6 +2320,11 @@ const (
|
||||
NFT_CT_AVGPKT = 0x10
|
||||
NFT_CT_ZONE = 0x11
|
||||
NFT_CT_EVENTMASK = 0x12
|
||||
NFT_CT_SRC_IP = 0x13
|
||||
NFT_CT_DST_IP = 0x14
|
||||
NFT_CT_SRC_IP6 = 0x15
|
||||
NFT_CT_DST_IP6 = 0x16
|
||||
NFT_CT_ID = 0x17
|
||||
NFTA_CT_UNSPEC = 0x0
|
||||
NFTA_CT_DREG = 0x1
|
||||
NFTA_CT_KEY = 0x2
|
||||
@@ -2594,8 +2605,8 @@ const (
|
||||
SOF_TIMESTAMPING_BIND_PHC = 0x8000
|
||||
SOF_TIMESTAMPING_OPT_ID_TCP = 0x10000
|
||||
|
||||
SOF_TIMESTAMPING_LAST = 0x20000
|
||||
SOF_TIMESTAMPING_MASK = 0x3ffff
|
||||
SOF_TIMESTAMPING_LAST = 0x40000
|
||||
SOF_TIMESTAMPING_MASK = 0x7ffff
|
||||
|
||||
SCM_TSTAMP_SND = 0x0
|
||||
SCM_TSTAMP_SCHED = 0x1
|
||||
@@ -3802,7 +3813,16 @@ const (
|
||||
ETHTOOL_MSG_PSE_GET = 0x24
|
||||
ETHTOOL_MSG_PSE_SET = 0x25
|
||||
ETHTOOL_MSG_RSS_GET = 0x26
|
||||
ETHTOOL_MSG_USER_MAX = 0x2d
|
||||
ETHTOOL_MSG_PLCA_GET_CFG = 0x27
|
||||
ETHTOOL_MSG_PLCA_SET_CFG = 0x28
|
||||
ETHTOOL_MSG_PLCA_GET_STATUS = 0x29
|
||||
ETHTOOL_MSG_MM_GET = 0x2a
|
||||
ETHTOOL_MSG_MM_SET = 0x2b
|
||||
ETHTOOL_MSG_MODULE_FW_FLASH_ACT = 0x2c
|
||||
ETHTOOL_MSG_PHY_GET = 0x2d
|
||||
ETHTOOL_MSG_TSCONFIG_GET = 0x2e
|
||||
ETHTOOL_MSG_TSCONFIG_SET = 0x2f
|
||||
ETHTOOL_MSG_USER_MAX = 0x2f
|
||||
ETHTOOL_MSG_KERNEL_NONE = 0x0
|
||||
ETHTOOL_MSG_STRSET_GET_REPLY = 0x1
|
||||
ETHTOOL_MSG_LINKINFO_GET_REPLY = 0x2
|
||||
@@ -3842,7 +3862,17 @@ const (
|
||||
ETHTOOL_MSG_MODULE_NTF = 0x24
|
||||
ETHTOOL_MSG_PSE_GET_REPLY = 0x25
|
||||
ETHTOOL_MSG_RSS_GET_REPLY = 0x26
|
||||
ETHTOOL_MSG_KERNEL_MAX = 0x2e
|
||||
ETHTOOL_MSG_PLCA_GET_CFG_REPLY = 0x27
|
||||
ETHTOOL_MSG_PLCA_GET_STATUS_REPLY = 0x28
|
||||
ETHTOOL_MSG_PLCA_NTF = 0x29
|
||||
ETHTOOL_MSG_MM_GET_REPLY = 0x2a
|
||||
ETHTOOL_MSG_MM_NTF = 0x2b
|
||||
ETHTOOL_MSG_MODULE_FW_FLASH_NTF = 0x2c
|
||||
ETHTOOL_MSG_PHY_GET_REPLY = 0x2d
|
||||
ETHTOOL_MSG_PHY_NTF = 0x2e
|
||||
ETHTOOL_MSG_TSCONFIG_GET_REPLY = 0x2f
|
||||
ETHTOOL_MSG_TSCONFIG_SET_REPLY = 0x30
|
||||
ETHTOOL_MSG_KERNEL_MAX = 0x30
|
||||
ETHTOOL_FLAG_COMPACT_BITSETS = 0x1
|
||||
ETHTOOL_FLAG_OMIT_REPLY = 0x2
|
||||
ETHTOOL_FLAG_STATS = 0x4
|
||||
@@ -3949,7 +3979,12 @@ const (
|
||||
ETHTOOL_A_RINGS_TCP_DATA_SPLIT = 0xb
|
||||
ETHTOOL_A_RINGS_CQE_SIZE = 0xc
|
||||
ETHTOOL_A_RINGS_TX_PUSH = 0xd
|
||||
ETHTOOL_A_RINGS_MAX = 0x10
|
||||
ETHTOOL_A_RINGS_RX_PUSH = 0xe
|
||||
ETHTOOL_A_RINGS_TX_PUSH_BUF_LEN = 0xf
|
||||
ETHTOOL_A_RINGS_TX_PUSH_BUF_LEN_MAX = 0x10
|
||||
ETHTOOL_A_RINGS_HDS_THRESH = 0x11
|
||||
ETHTOOL_A_RINGS_HDS_THRESH_MAX = 0x12
|
||||
ETHTOOL_A_RINGS_MAX = 0x12
|
||||
ETHTOOL_A_CHANNELS_UNSPEC = 0x0
|
||||
ETHTOOL_A_CHANNELS_HEADER = 0x1
|
||||
ETHTOOL_A_CHANNELS_RX_MAX = 0x2
|
||||
@@ -4015,7 +4050,9 @@ const (
|
||||
ETHTOOL_A_TSINFO_TX_TYPES = 0x3
|
||||
ETHTOOL_A_TSINFO_RX_FILTERS = 0x4
|
||||
ETHTOOL_A_TSINFO_PHC_INDEX = 0x5
|
||||
ETHTOOL_A_TSINFO_MAX = 0x6
|
||||
ETHTOOL_A_TSINFO_STATS = 0x6
|
||||
ETHTOOL_A_TSINFO_HWTSTAMP_PROVIDER = 0x7
|
||||
ETHTOOL_A_TSINFO_MAX = 0x9
|
||||
ETHTOOL_A_CABLE_TEST_UNSPEC = 0x0
|
||||
ETHTOOL_A_CABLE_TEST_HEADER = 0x1
|
||||
ETHTOOL_A_CABLE_TEST_MAX = 0x1
|
||||
@@ -4101,6 +4138,19 @@ const (
|
||||
ETHTOOL_A_TUNNEL_INFO_MAX = 0x2
|
||||
)
|
||||
|
||||
const (
|
||||
TCP_V4_FLOW = 0x1
|
||||
UDP_V4_FLOW = 0x2
|
||||
TCP_V6_FLOW = 0x5
|
||||
UDP_V6_FLOW = 0x6
|
||||
ESP_V4_FLOW = 0xa
|
||||
ESP_V6_FLOW = 0xc
|
||||
IP_USER_FLOW = 0xd
|
||||
IPV6_USER_FLOW = 0xe
|
||||
IPV6_FLOW = 0x11
|
||||
ETHER_FLOW = 0x12
|
||||
)
|
||||
|
||||
const SPEED_UNKNOWN = -0x1
|
||||
|
||||
type EthtoolDrvinfo struct {
|
||||
@@ -4613,6 +4663,7 @@ const (
|
||||
NL80211_ATTR_AKM_SUITES = 0x4c
|
||||
NL80211_ATTR_AP_ISOLATE = 0x60
|
||||
NL80211_ATTR_AP_SETTINGS_FLAGS = 0x135
|
||||
NL80211_ATTR_ASSOC_SPP_AMSDU = 0x14a
|
||||
NL80211_ATTR_AUTH_DATA = 0x9c
|
||||
NL80211_ATTR_AUTH_TYPE = 0x35
|
||||
NL80211_ATTR_BANDS = 0xef
|
||||
@@ -4623,6 +4674,7 @@ const (
|
||||
NL80211_ATTR_BSS_BASIC_RATES = 0x24
|
||||
NL80211_ATTR_BSS = 0x2f
|
||||
NL80211_ATTR_BSS_CTS_PROT = 0x1c
|
||||
NL80211_ATTR_BSS_DUMP_INCLUDE_USE_DATA = 0x147
|
||||
NL80211_ATTR_BSS_HT_OPMODE = 0x6d
|
||||
NL80211_ATTR_BSSID = 0xf5
|
||||
NL80211_ATTR_BSS_SELECT = 0xe3
|
||||
@@ -4682,6 +4734,7 @@ const (
|
||||
NL80211_ATTR_DTIM_PERIOD = 0xd
|
||||
NL80211_ATTR_DURATION = 0x57
|
||||
NL80211_ATTR_EHT_CAPABILITY = 0x136
|
||||
NL80211_ATTR_EMA_RNR_ELEMS = 0x145
|
||||
NL80211_ATTR_EML_CAPABILITY = 0x13d
|
||||
NL80211_ATTR_EXT_CAPA = 0xa9
|
||||
NL80211_ATTR_EXT_CAPA_MASK = 0xaa
|
||||
@@ -4717,6 +4770,7 @@ const (
|
||||
NL80211_ATTR_HIDDEN_SSID = 0x7e
|
||||
NL80211_ATTR_HT_CAPABILITY = 0x1f
|
||||
NL80211_ATTR_HT_CAPABILITY_MASK = 0x94
|
||||
NL80211_ATTR_HW_TIMESTAMP_ENABLED = 0x144
|
||||
NL80211_ATTR_IE_ASSOC_RESP = 0x80
|
||||
NL80211_ATTR_IE = 0x2a
|
||||
NL80211_ATTR_IE_PROBE_RESP = 0x7f
|
||||
@@ -4747,9 +4801,10 @@ const (
|
||||
NL80211_ATTR_MAC_HINT = 0xc8
|
||||
NL80211_ATTR_MAC_MASK = 0xd7
|
||||
NL80211_ATTR_MAX_AP_ASSOC_STA = 0xca
|
||||
NL80211_ATTR_MAX = 0x14d
|
||||
NL80211_ATTR_MAX = 0x151
|
||||
NL80211_ATTR_MAX_CRIT_PROT_DURATION = 0xb4
|
||||
NL80211_ATTR_MAX_CSA_COUNTERS = 0xce
|
||||
NL80211_ATTR_MAX_HW_TIMESTAMP_PEERS = 0x143
|
||||
NL80211_ATTR_MAX_MATCH_SETS = 0x85
|
||||
NL80211_ATTR_MAX_NUM_AKM_SUITES = 0x13c
|
||||
NL80211_ATTR_MAX_NUM_PMKIDS = 0x56
|
||||
@@ -4774,9 +4829,12 @@ const (
|
||||
NL80211_ATTR_MGMT_SUBTYPE = 0x29
|
||||
NL80211_ATTR_MLD_ADDR = 0x13a
|
||||
NL80211_ATTR_MLD_CAPA_AND_OPS = 0x13e
|
||||
NL80211_ATTR_MLO_LINK_DISABLED = 0x146
|
||||
NL80211_ATTR_MLO_LINK_ID = 0x139
|
||||
NL80211_ATTR_MLO_LINKS = 0x138
|
||||
NL80211_ATTR_MLO_SUPPORT = 0x13b
|
||||
NL80211_ATTR_MLO_TTLM_DLINK = 0x148
|
||||
NL80211_ATTR_MLO_TTLM_ULINK = 0x149
|
||||
NL80211_ATTR_MNTR_FLAGS = 0x17
|
||||
NL80211_ATTR_MPATH_INFO = 0x1b
|
||||
NL80211_ATTR_MPATH_NEXT_HOP = 0x1a
|
||||
@@ -4809,12 +4867,14 @@ const (
|
||||
NL80211_ATTR_PORT_AUTHORIZED = 0x103
|
||||
NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN = 0x5
|
||||
NL80211_ATTR_POWER_RULE_MAX_EIRP = 0x6
|
||||
NL80211_ATTR_POWER_RULE_PSD = 0x8
|
||||
NL80211_ATTR_PREV_BSSID = 0x4f
|
||||
NL80211_ATTR_PRIVACY = 0x46
|
||||
NL80211_ATTR_PROBE_RESP = 0x91
|
||||
NL80211_ATTR_PROBE_RESP_OFFLOAD = 0x90
|
||||
NL80211_ATTR_PROTOCOL_FEATURES = 0xad
|
||||
NL80211_ATTR_PS_STATE = 0x5d
|
||||
NL80211_ATTR_PUNCT_BITMAP = 0x142
|
||||
NL80211_ATTR_QOS_MAP = 0xc7
|
||||
NL80211_ATTR_RADAR_BACKGROUND = 0x134
|
||||
NL80211_ATTR_RADAR_EVENT = 0xa8
|
||||
@@ -4943,7 +5003,9 @@ const (
|
||||
NL80211_ATTR_WIPHY_FREQ = 0x26
|
||||
NL80211_ATTR_WIPHY_FREQ_HINT = 0xc9
|
||||
NL80211_ATTR_WIPHY_FREQ_OFFSET = 0x122
|
||||
NL80211_ATTR_WIPHY_INTERFACE_COMBINATIONS = 0x14c
|
||||
NL80211_ATTR_WIPHY_NAME = 0x2
|
||||
NL80211_ATTR_WIPHY_RADIOS = 0x14b
|
||||
NL80211_ATTR_WIPHY_RETRY_LONG = 0x3e
|
||||
NL80211_ATTR_WIPHY_RETRY_SHORT = 0x3d
|
||||
NL80211_ATTR_WIPHY_RTS_THRESHOLD = 0x40
|
||||
@@ -4978,6 +5040,8 @@ const (
|
||||
NL80211_BAND_ATTR_IFTYPE_DATA = 0x9
|
||||
NL80211_BAND_ATTR_MAX = 0xd
|
||||
NL80211_BAND_ATTR_RATES = 0x2
|
||||
NL80211_BAND_ATTR_S1G_CAPA = 0xd
|
||||
NL80211_BAND_ATTR_S1G_MCS_NSS_SET = 0xc
|
||||
NL80211_BAND_ATTR_VHT_CAPA = 0x8
|
||||
NL80211_BAND_ATTR_VHT_MCS_SET = 0x7
|
||||
NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC = 0x8
|
||||
@@ -5001,6 +5065,10 @@ const (
|
||||
NL80211_BSS_BEACON_INTERVAL = 0x4
|
||||
NL80211_BSS_BEACON_TSF = 0xd
|
||||
NL80211_BSS_BSSID = 0x1
|
||||
NL80211_BSS_CANNOT_USE_6GHZ_PWR_MISMATCH = 0x2
|
||||
NL80211_BSS_CANNOT_USE_NSTR_NONPRIMARY = 0x1
|
||||
NL80211_BSS_CANNOT_USE_REASONS = 0x18
|
||||
NL80211_BSS_CANNOT_USE_UHB_PWR_MISMATCH = 0x2
|
||||
NL80211_BSS_CAPABILITY = 0x5
|
||||
NL80211_BSS_CHAIN_SIGNAL = 0x13
|
||||
NL80211_BSS_CHAN_WIDTH_10 = 0x1
|
||||
@@ -5032,6 +5100,9 @@ const (
|
||||
NL80211_BSS_STATUS = 0x9
|
||||
NL80211_BSS_STATUS_IBSS_JOINED = 0x2
|
||||
NL80211_BSS_TSF = 0x3
|
||||
NL80211_BSS_USE_FOR = 0x17
|
||||
NL80211_BSS_USE_FOR_MLD_LINK = 0x2
|
||||
NL80211_BSS_USE_FOR_NORMAL = 0x1
|
||||
NL80211_CHAN_HT20 = 0x1
|
||||
NL80211_CHAN_HT40MINUS = 0x2
|
||||
NL80211_CHAN_HT40PLUS = 0x3
|
||||
@@ -5117,7 +5188,8 @@ const (
|
||||
NL80211_CMD_LEAVE_IBSS = 0x2c
|
||||
NL80211_CMD_LEAVE_MESH = 0x45
|
||||
NL80211_CMD_LEAVE_OCB = 0x6d
|
||||
NL80211_CMD_MAX = 0x9b
|
||||
NL80211_CMD_LINKS_REMOVED = 0x9a
|
||||
NL80211_CMD_MAX = 0x9d
|
||||
NL80211_CMD_MICHAEL_MIC_FAILURE = 0x29
|
||||
NL80211_CMD_MODIFY_LINK_STA = 0x97
|
||||
NL80211_CMD_NAN_MATCH = 0x78
|
||||
@@ -5161,6 +5233,7 @@ const (
|
||||
NL80211_CMD_SET_COALESCE = 0x65
|
||||
NL80211_CMD_SET_CQM = 0x3f
|
||||
NL80211_CMD_SET_FILS_AAD = 0x92
|
||||
NL80211_CMD_SET_HW_TIMESTAMP = 0x99
|
||||
NL80211_CMD_SET_INTERFACE = 0x6
|
||||
NL80211_CMD_SET_KEY = 0xa
|
||||
NL80211_CMD_SET_MAC_ACL = 0x5d
|
||||
@@ -5180,6 +5253,7 @@ const (
|
||||
NL80211_CMD_SET_SAR_SPECS = 0x8c
|
||||
NL80211_CMD_SET_STATION = 0x12
|
||||
NL80211_CMD_SET_TID_CONFIG = 0x89
|
||||
NL80211_CMD_SET_TID_TO_LINK_MAPPING = 0x9b
|
||||
NL80211_CMD_SET_TX_BITRATE_MASK = 0x39
|
||||
NL80211_CMD_SET_WDS_PEER = 0x42
|
||||
NL80211_CMD_SET_WIPHY = 0x2
|
||||
@@ -5247,6 +5321,7 @@ const (
|
||||
NL80211_EXT_FEATURE_AIRTIME_FAIRNESS = 0x21
|
||||
NL80211_EXT_FEATURE_AP_PMKSA_CACHING = 0x22
|
||||
NL80211_EXT_FEATURE_AQL = 0x28
|
||||
NL80211_EXT_FEATURE_AUTH_AND_DEAUTH_RANDOM_TA = 0x40
|
||||
NL80211_EXT_FEATURE_BEACON_PROTECTION_CLIENT = 0x2e
|
||||
NL80211_EXT_FEATURE_BEACON_PROTECTION = 0x29
|
||||
NL80211_EXT_FEATURE_BEACON_RATE_HE = 0x36
|
||||
@@ -5262,6 +5337,7 @@ const (
|
||||
NL80211_EXT_FEATURE_CQM_RSSI_LIST = 0xd
|
||||
NL80211_EXT_FEATURE_DATA_ACK_SIGNAL_SUPPORT = 0x1b
|
||||
NL80211_EXT_FEATURE_DEL_IBSS_STA = 0x2c
|
||||
NL80211_EXT_FEATURE_DFS_CONCURRENT = 0x43
|
||||
NL80211_EXT_FEATURE_DFS_OFFLOAD = 0x19
|
||||
NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER = 0x20
|
||||
NL80211_EXT_FEATURE_EXT_KEY_ID = 0x24
|
||||
@@ -5281,9 +5357,12 @@ const (
|
||||
NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION = 0x14
|
||||
NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE = 0x13
|
||||
NL80211_EXT_FEATURE_OPERATING_CHANNEL_VALIDATION = 0x31
|
||||
NL80211_EXT_FEATURE_OWE_OFFLOAD_AP = 0x42
|
||||
NL80211_EXT_FEATURE_OWE_OFFLOAD = 0x41
|
||||
NL80211_EXT_FEATURE_POWERED_ADDR_CHANGE = 0x3d
|
||||
NL80211_EXT_FEATURE_PROTECTED_TWT = 0x2b
|
||||
NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE = 0x39
|
||||
NL80211_EXT_FEATURE_PUNCT = 0x3e
|
||||
NL80211_EXT_FEATURE_RADAR_BACKGROUND = 0x3c
|
||||
NL80211_EXT_FEATURE_RRM = 0x1
|
||||
NL80211_EXT_FEATURE_SAE_OFFLOAD_AP = 0x33
|
||||
@@ -5295,8 +5374,10 @@ const (
|
||||
NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD = 0x23
|
||||
NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI = 0xc
|
||||
NL80211_EXT_FEATURE_SECURE_LTF = 0x37
|
||||
NL80211_EXT_FEATURE_SECURE_NAN = 0x3f
|
||||
NL80211_EXT_FEATURE_SECURE_RTT = 0x38
|
||||
NL80211_EXT_FEATURE_SET_SCAN_DWELL = 0x5
|
||||
NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT = 0x44
|
||||
NL80211_EXT_FEATURE_STA_TX_PWR = 0x25
|
||||
NL80211_EXT_FEATURE_TXQS = 0x1c
|
||||
NL80211_EXT_FEATURE_UNSOL_BCAST_PROBE_RESP = 0x35
|
||||
@@ -5343,7 +5424,10 @@ const (
|
||||
NL80211_FREQUENCY_ATTR_2MHZ = 0x16
|
||||
NL80211_FREQUENCY_ATTR_4MHZ = 0x17
|
||||
NL80211_FREQUENCY_ATTR_8MHZ = 0x18
|
||||
NL80211_FREQUENCY_ATTR_ALLOW_6GHZ_VLP_AP = 0x21
|
||||
NL80211_FREQUENCY_ATTR_CAN_MONITOR = 0x20
|
||||
NL80211_FREQUENCY_ATTR_DFS_CAC_TIME = 0xd
|
||||
NL80211_FREQUENCY_ATTR_DFS_CONCURRENT = 0x1d
|
||||
NL80211_FREQUENCY_ATTR_DFS_STATE = 0x7
|
||||
NL80211_FREQUENCY_ATTR_DFS_TIME = 0x8
|
||||
NL80211_FREQUENCY_ATTR_DISABLED = 0x2
|
||||
@@ -5351,12 +5435,14 @@ const (
|
||||
NL80211_FREQUENCY_ATTR_GO_CONCURRENT = 0xf
|
||||
NL80211_FREQUENCY_ATTR_INDOOR_ONLY = 0xe
|
||||
NL80211_FREQUENCY_ATTR_IR_CONCURRENT = 0xf
|
||||
NL80211_FREQUENCY_ATTR_MAX = 0x21
|
||||
NL80211_FREQUENCY_ATTR_MAX = 0x22
|
||||
NL80211_FREQUENCY_ATTR_MAX_TX_POWER = 0x6
|
||||
NL80211_FREQUENCY_ATTR_NO_10MHZ = 0x11
|
||||
NL80211_FREQUENCY_ATTR_NO_160MHZ = 0xc
|
||||
NL80211_FREQUENCY_ATTR_NO_20MHZ = 0x10
|
||||
NL80211_FREQUENCY_ATTR_NO_320MHZ = 0x1a
|
||||
NL80211_FREQUENCY_ATTR_NO_6GHZ_AFC_CLIENT = 0x1f
|
||||
NL80211_FREQUENCY_ATTR_NO_6GHZ_VLP_CLIENT = 0x1e
|
||||
NL80211_FREQUENCY_ATTR_NO_80MHZ = 0xb
|
||||
NL80211_FREQUENCY_ATTR_NO_EHT = 0x1b
|
||||
NL80211_FREQUENCY_ATTR_NO_HE = 0x13
|
||||
@@ -5364,8 +5450,11 @@ const (
|
||||
NL80211_FREQUENCY_ATTR_NO_HT40_PLUS = 0xa
|
||||
NL80211_FREQUENCY_ATTR_NO_IBSS = 0x3
|
||||
NL80211_FREQUENCY_ATTR_NO_IR = 0x3
|
||||
NL80211_FREQUENCY_ATTR_NO_UHB_AFC_CLIENT = 0x1f
|
||||
NL80211_FREQUENCY_ATTR_NO_UHB_VLP_CLIENT = 0x1e
|
||||
NL80211_FREQUENCY_ATTR_OFFSET = 0x14
|
||||
NL80211_FREQUENCY_ATTR_PASSIVE_SCAN = 0x3
|
||||
NL80211_FREQUENCY_ATTR_PSD = 0x1c
|
||||
NL80211_FREQUENCY_ATTR_RADAR = 0x5
|
||||
NL80211_FREQUENCY_ATTR_WMM = 0x12
|
||||
NL80211_FTM_RESP_ATTR_CIVICLOC = 0x3
|
||||
@@ -5430,6 +5519,7 @@ const (
|
||||
NL80211_IFTYPE_STATION = 0x2
|
||||
NL80211_IFTYPE_UNSPECIFIED = 0x0
|
||||
NL80211_IFTYPE_WDS = 0x5
|
||||
NL80211_KCK_EXT_LEN_32 = 0x20
|
||||
NL80211_KCK_EXT_LEN = 0x18
|
||||
NL80211_KCK_LEN = 0x10
|
||||
NL80211_KEK_EXT_LEN = 0x20
|
||||
@@ -5458,9 +5548,10 @@ const (
|
||||
NL80211_MAX_SUPP_HT_RATES = 0x4d
|
||||
NL80211_MAX_SUPP_RATES = 0x20
|
||||
NL80211_MAX_SUPP_REG_RULES = 0x80
|
||||
NL80211_MAX_SUPP_SELECTORS = 0x80
|
||||
NL80211_MBSSID_CONFIG_ATTR_EMA = 0x5
|
||||
NL80211_MBSSID_CONFIG_ATTR_INDEX = 0x3
|
||||
NL80211_MBSSID_CONFIG_ATTR_MAX = 0x5
|
||||
NL80211_MBSSID_CONFIG_ATTR_MAX = 0x6
|
||||
NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY = 0x2
|
||||
NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES = 0x1
|
||||
NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX = 0x4
|
||||
@@ -5703,11 +5794,16 @@ const (
|
||||
NL80211_RADAR_PRE_CAC_EXPIRED = 0x4
|
||||
NL80211_RATE_INFO_10_MHZ_WIDTH = 0xb
|
||||
NL80211_RATE_INFO_160_MHZ_WIDTH = 0xa
|
||||
NL80211_RATE_INFO_16_MHZ_WIDTH = 0x1d
|
||||
NL80211_RATE_INFO_1_MHZ_WIDTH = 0x19
|
||||
NL80211_RATE_INFO_2_MHZ_WIDTH = 0x1a
|
||||
NL80211_RATE_INFO_320_MHZ_WIDTH = 0x12
|
||||
NL80211_RATE_INFO_40_MHZ_WIDTH = 0x3
|
||||
NL80211_RATE_INFO_4_MHZ_WIDTH = 0x1b
|
||||
NL80211_RATE_INFO_5_MHZ_WIDTH = 0xc
|
||||
NL80211_RATE_INFO_80_MHZ_WIDTH = 0x8
|
||||
NL80211_RATE_INFO_80P80_MHZ_WIDTH = 0x9
|
||||
NL80211_RATE_INFO_8_MHZ_WIDTH = 0x1c
|
||||
NL80211_RATE_INFO_BITRATE32 = 0x5
|
||||
NL80211_RATE_INFO_BITRATE = 0x1
|
||||
NL80211_RATE_INFO_EHT_GI_0_8 = 0x0
|
||||
@@ -5753,6 +5849,8 @@ const (
|
||||
NL80211_RATE_INFO_HE_RU_ALLOC = 0x11
|
||||
NL80211_RATE_INFO_MAX = 0x1d
|
||||
NL80211_RATE_INFO_MCS = 0x2
|
||||
NL80211_RATE_INFO_S1G_MCS = 0x17
|
||||
NL80211_RATE_INFO_S1G_NSS = 0x18
|
||||
NL80211_RATE_INFO_SHORT_GI = 0x4
|
||||
NL80211_RATE_INFO_VHT_MCS = 0x6
|
||||
NL80211_RATE_INFO_VHT_NSS = 0x7
|
||||
@@ -5770,14 +5868,19 @@ const (
|
||||
NL80211_REKEY_DATA_KEK = 0x1
|
||||
NL80211_REKEY_DATA_REPLAY_CTR = 0x3
|
||||
NL80211_REPLAY_CTR_LEN = 0x8
|
||||
NL80211_RRF_ALLOW_6GHZ_VLP_AP = 0x1000000
|
||||
NL80211_RRF_AUTO_BW = 0x800
|
||||
NL80211_RRF_DFS = 0x10
|
||||
NL80211_RRF_DFS_CONCURRENT = 0x200000
|
||||
NL80211_RRF_GO_CONCURRENT = 0x1000
|
||||
NL80211_RRF_IR_CONCURRENT = 0x1000
|
||||
NL80211_RRF_NO_160MHZ = 0x10000
|
||||
NL80211_RRF_NO_320MHZ = 0x40000
|
||||
NL80211_RRF_NO_6GHZ_AFC_CLIENT = 0x800000
|
||||
NL80211_RRF_NO_6GHZ_VLP_CLIENT = 0x400000
|
||||
NL80211_RRF_NO_80MHZ = 0x8000
|
||||
NL80211_RRF_NO_CCK = 0x2
|
||||
NL80211_RRF_NO_EHT = 0x80000
|
||||
NL80211_RRF_NO_HE = 0x20000
|
||||
NL80211_RRF_NO_HT40 = 0x6000
|
||||
NL80211_RRF_NO_HT40MINUS = 0x2000
|
||||
@@ -5788,7 +5891,10 @@ const (
|
||||
NL80211_RRF_NO_IR = 0x80
|
||||
NL80211_RRF_NO_OFDM = 0x1
|
||||
NL80211_RRF_NO_OUTDOOR = 0x8
|
||||
NL80211_RRF_NO_UHB_AFC_CLIENT = 0x800000
|
||||
NL80211_RRF_NO_UHB_VLP_CLIENT = 0x400000
|
||||
NL80211_RRF_PASSIVE_SCAN = 0x80
|
||||
NL80211_RRF_PSD = 0x100000
|
||||
NL80211_RRF_PTMP_ONLY = 0x40
|
||||
NL80211_RRF_PTP_ONLY = 0x20
|
||||
NL80211_RXMGMT_FLAG_ANSWERED = 0x1
|
||||
@@ -5849,6 +5955,7 @@ const (
|
||||
NL80211_STA_FLAG_MAX_OLD_API = 0x6
|
||||
NL80211_STA_FLAG_MFP = 0x4
|
||||
NL80211_STA_FLAG_SHORT_PREAMBLE = 0x2
|
||||
NL80211_STA_FLAG_SPP_AMSDU = 0x8
|
||||
NL80211_STA_FLAG_TDLS_PEER = 0x6
|
||||
NL80211_STA_FLAG_WME = 0x3
|
||||
NL80211_STA_INFO_ACK_SIGNAL_AVG = 0x23
|
||||
@@ -6007,6 +6114,13 @@ const (
|
||||
NL80211_VHT_CAPABILITY_LEN = 0xc
|
||||
NL80211_VHT_NSS_MAX = 0x8
|
||||
NL80211_WIPHY_NAME_MAXLEN = 0x40
|
||||
NL80211_WIPHY_RADIO_ATTR_FREQ_RANGE = 0x2
|
||||
NL80211_WIPHY_RADIO_ATTR_INDEX = 0x1
|
||||
NL80211_WIPHY_RADIO_ATTR_INTERFACE_COMBINATION = 0x3
|
||||
NL80211_WIPHY_RADIO_ATTR_MAX = 0x4
|
||||
NL80211_WIPHY_RADIO_FREQ_ATTR_END = 0x2
|
||||
NL80211_WIPHY_RADIO_FREQ_ATTR_MAX = 0x2
|
||||
NL80211_WIPHY_RADIO_FREQ_ATTR_START = 0x1
|
||||
NL80211_WMMR_AIFSN = 0x3
|
||||
NL80211_WMMR_CW_MAX = 0x2
|
||||
NL80211_WMMR_CW_MIN = 0x1
|
||||
@@ -6038,6 +6152,7 @@ const (
|
||||
NL80211_WOWLAN_TRIG_PKT_PATTERN = 0x4
|
||||
NL80211_WOWLAN_TRIG_RFKILL_RELEASE = 0x9
|
||||
NL80211_WOWLAN_TRIG_TCP_CONNECTION = 0xe
|
||||
NL80211_WOWLAN_TRIG_UNPROTECTED_DEAUTH_DISASSOC = 0x14
|
||||
NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211 = 0xa
|
||||
NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN = 0xb
|
||||
NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023 = 0xc
|
||||
|
||||
+17
-1
@@ -282,7 +282,7 @@ type Taskstats struct {
|
||||
Ac_exitcode uint32
|
||||
Ac_flag uint8
|
||||
Ac_nice uint8
|
||||
_ [4]byte
|
||||
_ [6]byte
|
||||
Cpu_count uint64
|
||||
Cpu_delay_total uint64
|
||||
Blkio_count uint64
|
||||
@@ -338,6 +338,22 @@ type Taskstats struct {
|
||||
Wpcopy_delay_total uint64
|
||||
Irq_count uint64
|
||||
Irq_delay_total uint64
|
||||
Cpu_delay_max uint64
|
||||
Cpu_delay_min uint64
|
||||
Blkio_delay_max uint64
|
||||
Blkio_delay_min uint64
|
||||
Swapin_delay_max uint64
|
||||
Swapin_delay_min uint64
|
||||
Freepages_delay_max uint64
|
||||
Freepages_delay_min uint64
|
||||
Thrashing_delay_max uint64
|
||||
Thrashing_delay_min uint64
|
||||
Compact_delay_max uint64
|
||||
Compact_delay_min uint64
|
||||
Wpcopy_delay_max uint64
|
||||
Wpcopy_delay_min uint64
|
||||
Irq_delay_max uint64
|
||||
Irq_delay_min uint64
|
||||
}
|
||||
|
||||
type cpuMask uint32
|
||||
|
||||
+16
@@ -351,6 +351,22 @@ type Taskstats struct {
|
||||
Wpcopy_delay_total uint64
|
||||
Irq_count uint64
|
||||
Irq_delay_total uint64
|
||||
Cpu_delay_max uint64
|
||||
Cpu_delay_min uint64
|
||||
Blkio_delay_max uint64
|
||||
Blkio_delay_min uint64
|
||||
Swapin_delay_max uint64
|
||||
Swapin_delay_min uint64
|
||||
Freepages_delay_max uint64
|
||||
Freepages_delay_min uint64
|
||||
Thrashing_delay_max uint64
|
||||
Thrashing_delay_min uint64
|
||||
Compact_delay_max uint64
|
||||
Compact_delay_min uint64
|
||||
Wpcopy_delay_max uint64
|
||||
Wpcopy_delay_min uint64
|
||||
Irq_delay_max uint64
|
||||
Irq_delay_min uint64
|
||||
}
|
||||
|
||||
type cpuMask uint64
|
||||
|
||||
+18
-2
@@ -91,7 +91,7 @@ type Stat_t struct {
|
||||
Gid uint32
|
||||
Rdev uint64
|
||||
_ uint16
|
||||
_ [4]byte
|
||||
_ [6]byte
|
||||
Size int64
|
||||
Blksize int32
|
||||
_ [4]byte
|
||||
@@ -273,7 +273,7 @@ type Taskstats struct {
|
||||
Ac_exitcode uint32
|
||||
Ac_flag uint8
|
||||
Ac_nice uint8
|
||||
_ [4]byte
|
||||
_ [6]byte
|
||||
Cpu_count uint64
|
||||
Cpu_delay_total uint64
|
||||
Blkio_count uint64
|
||||
@@ -329,6 +329,22 @@ type Taskstats struct {
|
||||
Wpcopy_delay_total uint64
|
||||
Irq_count uint64
|
||||
Irq_delay_total uint64
|
||||
Cpu_delay_max uint64
|
||||
Cpu_delay_min uint64
|
||||
Blkio_delay_max uint64
|
||||
Blkio_delay_min uint64
|
||||
Swapin_delay_max uint64
|
||||
Swapin_delay_min uint64
|
||||
Freepages_delay_max uint64
|
||||
Freepages_delay_min uint64
|
||||
Thrashing_delay_max uint64
|
||||
Thrashing_delay_min uint64
|
||||
Compact_delay_max uint64
|
||||
Compact_delay_min uint64
|
||||
Wpcopy_delay_max uint64
|
||||
Wpcopy_delay_min uint64
|
||||
Irq_delay_max uint64
|
||||
Irq_delay_min uint64
|
||||
}
|
||||
|
||||
type cpuMask uint32
|
||||
|
||||
+16
@@ -330,6 +330,22 @@ type Taskstats struct {
|
||||
Wpcopy_delay_total uint64
|
||||
Irq_count uint64
|
||||
Irq_delay_total uint64
|
||||
Cpu_delay_max uint64
|
||||
Cpu_delay_min uint64
|
||||
Blkio_delay_max uint64
|
||||
Blkio_delay_min uint64
|
||||
Swapin_delay_max uint64
|
||||
Swapin_delay_min uint64
|
||||
Freepages_delay_max uint64
|
||||
Freepages_delay_min uint64
|
||||
Thrashing_delay_max uint64
|
||||
Thrashing_delay_min uint64
|
||||
Compact_delay_max uint64
|
||||
Compact_delay_min uint64
|
||||
Wpcopy_delay_max uint64
|
||||
Wpcopy_delay_min uint64
|
||||
Irq_delay_max uint64
|
||||
Irq_delay_min uint64
|
||||
}
|
||||
|
||||
type cpuMask uint64
|
||||
|
||||
+16
@@ -331,6 +331,22 @@ type Taskstats struct {
|
||||
Wpcopy_delay_total uint64
|
||||
Irq_count uint64
|
||||
Irq_delay_total uint64
|
||||
Cpu_delay_max uint64
|
||||
Cpu_delay_min uint64
|
||||
Blkio_delay_max uint64
|
||||
Blkio_delay_min uint64
|
||||
Swapin_delay_max uint64
|
||||
Swapin_delay_min uint64
|
||||
Freepages_delay_max uint64
|
||||
Freepages_delay_min uint64
|
||||
Thrashing_delay_max uint64
|
||||
Thrashing_delay_min uint64
|
||||
Compact_delay_max uint64
|
||||
Compact_delay_min uint64
|
||||
Wpcopy_delay_max uint64
|
||||
Wpcopy_delay_min uint64
|
||||
Irq_delay_max uint64
|
||||
Irq_delay_min uint64
|
||||
}
|
||||
|
||||
type cpuMask uint64
|
||||
|
||||
+17
-1
@@ -278,7 +278,7 @@ type Taskstats struct {
|
||||
Ac_exitcode uint32
|
||||
Ac_flag uint8
|
||||
Ac_nice uint8
|
||||
_ [4]byte
|
||||
_ [6]byte
|
||||
Cpu_count uint64
|
||||
Cpu_delay_total uint64
|
||||
Blkio_count uint64
|
||||
@@ -334,6 +334,22 @@ type Taskstats struct {
|
||||
Wpcopy_delay_total uint64
|
||||
Irq_count uint64
|
||||
Irq_delay_total uint64
|
||||
Cpu_delay_max uint64
|
||||
Cpu_delay_min uint64
|
||||
Blkio_delay_max uint64
|
||||
Blkio_delay_min uint64
|
||||
Swapin_delay_max uint64
|
||||
Swapin_delay_min uint64
|
||||
Freepages_delay_max uint64
|
||||
Freepages_delay_min uint64
|
||||
Thrashing_delay_max uint64
|
||||
Thrashing_delay_min uint64
|
||||
Compact_delay_max uint64
|
||||
Compact_delay_min uint64
|
||||
Wpcopy_delay_max uint64
|
||||
Wpcopy_delay_min uint64
|
||||
Irq_delay_max uint64
|
||||
Irq_delay_min uint64
|
||||
}
|
||||
|
||||
type cpuMask uint32
|
||||
|
||||
+16
@@ -333,6 +333,22 @@ type Taskstats struct {
|
||||
Wpcopy_delay_total uint64
|
||||
Irq_count uint64
|
||||
Irq_delay_total uint64
|
||||
Cpu_delay_max uint64
|
||||
Cpu_delay_min uint64
|
||||
Blkio_delay_max uint64
|
||||
Blkio_delay_min uint64
|
||||
Swapin_delay_max uint64
|
||||
Swapin_delay_min uint64
|
||||
Freepages_delay_max uint64
|
||||
Freepages_delay_min uint64
|
||||
Thrashing_delay_max uint64
|
||||
Thrashing_delay_min uint64
|
||||
Compact_delay_max uint64
|
||||
Compact_delay_min uint64
|
||||
Wpcopy_delay_max uint64
|
||||
Wpcopy_delay_min uint64
|
||||
Irq_delay_max uint64
|
||||
Irq_delay_min uint64
|
||||
}
|
||||
|
||||
type cpuMask uint64
|
||||
|
||||
+16
@@ -333,6 +333,22 @@ type Taskstats struct {
|
||||
Wpcopy_delay_total uint64
|
||||
Irq_count uint64
|
||||
Irq_delay_total uint64
|
||||
Cpu_delay_max uint64
|
||||
Cpu_delay_min uint64
|
||||
Blkio_delay_max uint64
|
||||
Blkio_delay_min uint64
|
||||
Swapin_delay_max uint64
|
||||
Swapin_delay_min uint64
|
||||
Freepages_delay_max uint64
|
||||
Freepages_delay_min uint64
|
||||
Thrashing_delay_max uint64
|
||||
Thrashing_delay_min uint64
|
||||
Compact_delay_max uint64
|
||||
Compact_delay_min uint64
|
||||
Wpcopy_delay_max uint64
|
||||
Wpcopy_delay_min uint64
|
||||
Irq_delay_max uint64
|
||||
Irq_delay_min uint64
|
||||
}
|
||||
|
||||
type cpuMask uint64
|
||||
|
||||
+17
-1
@@ -278,7 +278,7 @@ type Taskstats struct {
|
||||
Ac_exitcode uint32
|
||||
Ac_flag uint8
|
||||
Ac_nice uint8
|
||||
_ [4]byte
|
||||
_ [6]byte
|
||||
Cpu_count uint64
|
||||
Cpu_delay_total uint64
|
||||
Blkio_count uint64
|
||||
@@ -334,6 +334,22 @@ type Taskstats struct {
|
||||
Wpcopy_delay_total uint64
|
||||
Irq_count uint64
|
||||
Irq_delay_total uint64
|
||||
Cpu_delay_max uint64
|
||||
Cpu_delay_min uint64
|
||||
Blkio_delay_max uint64
|
||||
Blkio_delay_min uint64
|
||||
Swapin_delay_max uint64
|
||||
Swapin_delay_min uint64
|
||||
Freepages_delay_max uint64
|
||||
Freepages_delay_min uint64
|
||||
Thrashing_delay_max uint64
|
||||
Thrashing_delay_min uint64
|
||||
Compact_delay_max uint64
|
||||
Compact_delay_min uint64
|
||||
Wpcopy_delay_max uint64
|
||||
Wpcopy_delay_min uint64
|
||||
Irq_delay_max uint64
|
||||
Irq_delay_min uint64
|
||||
}
|
||||
|
||||
type cpuMask uint32
|
||||
|
||||
+18
-2
@@ -90,7 +90,7 @@ type Stat_t struct {
|
||||
Gid uint32
|
||||
Rdev uint64
|
||||
_ uint16
|
||||
_ [4]byte
|
||||
_ [6]byte
|
||||
Size int64
|
||||
Blksize int32
|
||||
_ [4]byte
|
||||
@@ -285,7 +285,7 @@ type Taskstats struct {
|
||||
Ac_exitcode uint32
|
||||
Ac_flag uint8
|
||||
Ac_nice uint8
|
||||
_ [4]byte
|
||||
_ [6]byte
|
||||
Cpu_count uint64
|
||||
Cpu_delay_total uint64
|
||||
Blkio_count uint64
|
||||
@@ -341,6 +341,22 @@ type Taskstats struct {
|
||||
Wpcopy_delay_total uint64
|
||||
Irq_count uint64
|
||||
Irq_delay_total uint64
|
||||
Cpu_delay_max uint64
|
||||
Cpu_delay_min uint64
|
||||
Blkio_delay_max uint64
|
||||
Blkio_delay_min uint64
|
||||
Swapin_delay_max uint64
|
||||
Swapin_delay_min uint64
|
||||
Freepages_delay_max uint64
|
||||
Freepages_delay_min uint64
|
||||
Thrashing_delay_max uint64
|
||||
Thrashing_delay_min uint64
|
||||
Compact_delay_max uint64
|
||||
Compact_delay_min uint64
|
||||
Wpcopy_delay_max uint64
|
||||
Wpcopy_delay_min uint64
|
||||
Irq_delay_max uint64
|
||||
Irq_delay_min uint64
|
||||
}
|
||||
|
||||
type cpuMask uint32
|
||||
|
||||
+16
@@ -340,6 +340,22 @@ type Taskstats struct {
|
||||
Wpcopy_delay_total uint64
|
||||
Irq_count uint64
|
||||
Irq_delay_total uint64
|
||||
Cpu_delay_max uint64
|
||||
Cpu_delay_min uint64
|
||||
Blkio_delay_max uint64
|
||||
Blkio_delay_min uint64
|
||||
Swapin_delay_max uint64
|
||||
Swapin_delay_min uint64
|
||||
Freepages_delay_max uint64
|
||||
Freepages_delay_min uint64
|
||||
Thrashing_delay_max uint64
|
||||
Thrashing_delay_min uint64
|
||||
Compact_delay_max uint64
|
||||
Compact_delay_min uint64
|
||||
Wpcopy_delay_max uint64
|
||||
Wpcopy_delay_min uint64
|
||||
Irq_delay_max uint64
|
||||
Irq_delay_min uint64
|
||||
}
|
||||
|
||||
type cpuMask uint64
|
||||
|
||||
+16
@@ -340,6 +340,22 @@ type Taskstats struct {
|
||||
Wpcopy_delay_total uint64
|
||||
Irq_count uint64
|
||||
Irq_delay_total uint64
|
||||
Cpu_delay_max uint64
|
||||
Cpu_delay_min uint64
|
||||
Blkio_delay_max uint64
|
||||
Blkio_delay_min uint64
|
||||
Swapin_delay_max uint64
|
||||
Swapin_delay_min uint64
|
||||
Freepages_delay_max uint64
|
||||
Freepages_delay_min uint64
|
||||
Thrashing_delay_max uint64
|
||||
Thrashing_delay_min uint64
|
||||
Compact_delay_max uint64
|
||||
Compact_delay_min uint64
|
||||
Wpcopy_delay_max uint64
|
||||
Wpcopy_delay_min uint64
|
||||
Irq_delay_max uint64
|
||||
Irq_delay_min uint64
|
||||
}
|
||||
|
||||
type cpuMask uint64
|
||||
|
||||
+16
@@ -358,6 +358,22 @@ type Taskstats struct {
|
||||
Wpcopy_delay_total uint64
|
||||
Irq_count uint64
|
||||
Irq_delay_total uint64
|
||||
Cpu_delay_max uint64
|
||||
Cpu_delay_min uint64
|
||||
Blkio_delay_max uint64
|
||||
Blkio_delay_min uint64
|
||||
Swapin_delay_max uint64
|
||||
Swapin_delay_min uint64
|
||||
Freepages_delay_max uint64
|
||||
Freepages_delay_min uint64
|
||||
Thrashing_delay_max uint64
|
||||
Thrashing_delay_min uint64
|
||||
Compact_delay_max uint64
|
||||
Compact_delay_min uint64
|
||||
Wpcopy_delay_max uint64
|
||||
Wpcopy_delay_min uint64
|
||||
Irq_delay_max uint64
|
||||
Irq_delay_min uint64
|
||||
}
|
||||
|
||||
type cpuMask uint64
|
||||
|
||||
+16
@@ -353,6 +353,22 @@ type Taskstats struct {
|
||||
Wpcopy_delay_total uint64
|
||||
Irq_count uint64
|
||||
Irq_delay_total uint64
|
||||
Cpu_delay_max uint64
|
||||
Cpu_delay_min uint64
|
||||
Blkio_delay_max uint64
|
||||
Blkio_delay_min uint64
|
||||
Swapin_delay_max uint64
|
||||
Swapin_delay_min uint64
|
||||
Freepages_delay_max uint64
|
||||
Freepages_delay_min uint64
|
||||
Thrashing_delay_max uint64
|
||||
Thrashing_delay_min uint64
|
||||
Compact_delay_max uint64
|
||||
Compact_delay_min uint64
|
||||
Wpcopy_delay_max uint64
|
||||
Wpcopy_delay_min uint64
|
||||
Irq_delay_max uint64
|
||||
Irq_delay_min uint64
|
||||
}
|
||||
|
||||
type cpuMask uint64
|
||||
|
||||
+16
@@ -335,6 +335,22 @@ type Taskstats struct {
|
||||
Wpcopy_delay_total uint64
|
||||
Irq_count uint64
|
||||
Irq_delay_total uint64
|
||||
Cpu_delay_max uint64
|
||||
Cpu_delay_min uint64
|
||||
Blkio_delay_max uint64
|
||||
Blkio_delay_min uint64
|
||||
Swapin_delay_max uint64
|
||||
Swapin_delay_min uint64
|
||||
Freepages_delay_max uint64
|
||||
Freepages_delay_min uint64
|
||||
Thrashing_delay_max uint64
|
||||
Thrashing_delay_min uint64
|
||||
Compact_delay_max uint64
|
||||
Compact_delay_min uint64
|
||||
Wpcopy_delay_max uint64
|
||||
Wpcopy_delay_min uint64
|
||||
Irq_delay_max uint64
|
||||
Irq_delay_min uint64
|
||||
}
|
||||
|
||||
type cpuMask uint64
|
||||
|
||||
+44
-5
@@ -1303,7 +1303,10 @@ func (selfRelativeSD *SECURITY_DESCRIPTOR) ToAbsolute() (absoluteSD *SECURITY_DE
|
||||
return nil, err
|
||||
}
|
||||
if absoluteSDSize > 0 {
|
||||
absoluteSD = (*SECURITY_DESCRIPTOR)(unsafe.Pointer(&make([]byte, absoluteSDSize)[0]))
|
||||
absoluteSD = new(SECURITY_DESCRIPTOR)
|
||||
if unsafe.Sizeof(*absoluteSD) < uintptr(absoluteSDSize) {
|
||||
panic("sizeof(SECURITY_DESCRIPTOR) too small")
|
||||
}
|
||||
}
|
||||
var (
|
||||
dacl *ACL
|
||||
@@ -1312,19 +1315,55 @@ func (selfRelativeSD *SECURITY_DESCRIPTOR) ToAbsolute() (absoluteSD *SECURITY_DE
|
||||
group *SID
|
||||
)
|
||||
if daclSize > 0 {
|
||||
dacl = (*ACL)(unsafe.Pointer(&make([]byte, daclSize)[0]))
|
||||
dacl = (*ACL)(unsafe.Pointer(unsafe.SliceData(make([]byte, daclSize))))
|
||||
}
|
||||
if saclSize > 0 {
|
||||
sacl = (*ACL)(unsafe.Pointer(&make([]byte, saclSize)[0]))
|
||||
sacl = (*ACL)(unsafe.Pointer(unsafe.SliceData(make([]byte, saclSize))))
|
||||
}
|
||||
if ownerSize > 0 {
|
||||
owner = (*SID)(unsafe.Pointer(&make([]byte, ownerSize)[0]))
|
||||
owner = (*SID)(unsafe.Pointer(unsafe.SliceData(make([]byte, ownerSize))))
|
||||
}
|
||||
if groupSize > 0 {
|
||||
group = (*SID)(unsafe.Pointer(&make([]byte, groupSize)[0]))
|
||||
group = (*SID)(unsafe.Pointer(unsafe.SliceData(make([]byte, groupSize))))
|
||||
}
|
||||
// We call into Windows via makeAbsoluteSD, which sets up
|
||||
// pointers within absoluteSD that point to other chunks of memory
|
||||
// we pass into makeAbsoluteSD, and that happens outside the view of the GC.
|
||||
// We therefore take some care here to then verify the pointers are as we expect
|
||||
// and set them explicitly in view of the GC. See https://go.dev/issue/73199.
|
||||
// TODO: consider weak pointers once Go 1.24 is appropriate. See suggestion in https://go.dev/cl/663575.
|
||||
err = makeAbsoluteSD(selfRelativeSD, absoluteSD, &absoluteSDSize,
|
||||
dacl, &daclSize, sacl, &saclSize, owner, &ownerSize, group, &groupSize)
|
||||
if err != nil {
|
||||
// Don't return absoluteSD, which might be partially initialized.
|
||||
return nil, err
|
||||
}
|
||||
// Before using any fields, verify absoluteSD is in the format we expect according to Windows.
|
||||
// See https://learn.microsoft.com/en-us/windows/win32/secauthz/absolute-and-self-relative-security-descriptors
|
||||
absControl, _, err := absoluteSD.Control()
|
||||
if err != nil {
|
||||
panic("absoluteSD: " + err.Error())
|
||||
}
|
||||
if absControl&SE_SELF_RELATIVE != 0 {
|
||||
panic("absoluteSD not in absolute format")
|
||||
}
|
||||
if absoluteSD.dacl != dacl {
|
||||
panic("dacl pointer mismatch")
|
||||
}
|
||||
if absoluteSD.sacl != sacl {
|
||||
panic("sacl pointer mismatch")
|
||||
}
|
||||
if absoluteSD.owner != owner {
|
||||
panic("owner pointer mismatch")
|
||||
}
|
||||
if absoluteSD.group != group {
|
||||
panic("group pointer mismatch")
|
||||
}
|
||||
absoluteSD.dacl = dacl
|
||||
absoluteSD.sacl = sacl
|
||||
absoluteSD.owner = owner
|
||||
absoluteSD.group = group
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user