Merge branch 'main' into user-account
This commit is contained in:
commit
0df3e3e4f5
9 changed files with 1059 additions and 12764 deletions
|
@ -847,7 +847,7 @@ to `https://ntfy.example.com/v1/account/billing/webhook`.
|
||||||
Here's an example:
|
Here's an example:
|
||||||
|
|
||||||
``` yaml
|
``` yaml
|
||||||
stripe-secret-key: "sk_live_ZmhzZGtmbGhkc2tqZmhzYcO2a2hmbGtnaHNkbGtnaGRsc2hnbG"
|
stripe-secret-key: "sk_test_ZmhzZGtmbGhkc2tqZmhzYcO2a2hmbGtnaHNkbGtnaGRsc2hnbG"
|
||||||
stripe-webhook-key: "whsec_ZnNkZnNIRExBSFNES0hBRFNmaHNka2ZsaGR"
|
stripe-webhook-key: "whsec_ZnNkZnNIRExBSFNES0hBRFNmaHNka2ZsaGR"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -327,7 +327,76 @@ To build your own version with Firebase, you must:
|
||||||
```
|
```
|
||||||
|
|
||||||
## iOS app
|
## iOS app
|
||||||
The ntfy iOS app source code is available [on GitHub](https://github.com/binwiederhier/ntfy-ios).
|
Building the iOS app is very involved. Please report any inconsistencies or issues with it. The requirements are
|
||||||
|
strictly based off of my development on this app. There may be other versions of macOS / XCode that work.
|
||||||
|
|
||||||
|
### Requirements
|
||||||
|
1. macOS Monterey or later
|
||||||
|
1. XCode 13.2+
|
||||||
|
1. A physical iOS device (for push notifications, Firebase does not work in the XCode simulator)
|
||||||
|
1. Firebase account
|
||||||
|
1. Apple Developer license? (I forget if it's possible to do testing without purchasing the license)
|
||||||
|
|
||||||
|
### Apple setup
|
||||||
|
|
||||||
!!! info
|
!!! info
|
||||||
I haven't had time to move the build instructions here. Please check out the repository instead.
|
Along with this step, the [PLIST Deployment](#plist-deployment-and-configuration) step is also required
|
||||||
|
for these changes to take effect in the iOS app.
|
||||||
|
|
||||||
|
1. [Create a new key in Apple Developer Member Center](https://developer.apple.com/account/resources/authkeys/add)
|
||||||
|
1. Select "Apple Push Notifications service (APNs)"
|
||||||
|
1. Download the newly created key (should have a file name similar to `AuthKey_ZZZZZZ.p8`, where `ZZZZZZ` is the **Key ID**)
|
||||||
|
1. Record your **Team ID** - it can be seen in the top-right corner of the page, or on your Account > Membership page
|
||||||
|
1. Next, navigate to "Project Settings" in the firebase console for your project, and select the iOS app you created. Then, click "Cloud Messaging" in the left sidebar, and scroll down to the "APNs Authentication Key" section. Click "Upload Key", and upload the key you downloaded from Apple Developer.
|
||||||
|
|
||||||
|
!!! warning
|
||||||
|
If you don't do the above setups for APNS, **notifications will not post instantly or sometimes at all**. This is because of the missing APNS key, which is required for firebase to send notifications to the iOS app. See below for a snip from the firebase docs.
|
||||||
|
|
||||||
|
If you don't have an APNs authentication key, you can still send notifications to iOS devices, but they won't be delivered
|
||||||
|
instantly. Instead, they'll be delivered when the device wakes up to check for new notifications or when your application
|
||||||
|
sends a firebase request to check for them. The time to check for new notifications can vary from a few seconds to hours,
|
||||||
|
days or even weeks. Enabling APNs authentication keys ensures that notifications are delivered instantly and is strongly
|
||||||
|
recommended.
|
||||||
|
|
||||||
|
### Firebase setup
|
||||||
|
|
||||||
|
1. If you haven't already, create a Google / Firebase account
|
||||||
|
1. Visit the [Firebase console](https://console.firebase.google.com)
|
||||||
|
1. Create a new Firebase project:
|
||||||
|
1. Enter a project name
|
||||||
|
1. Disable Google Analytics (currently iOS app does not support analytics)
|
||||||
|
1. On the "Project settings" page, add an iOS app
|
||||||
|
1. Apple bundle ID - "com.copephobia.ntfy-ios" (this can be changed to match XCode's ntfy.sh target > "Bundle Identifier" value)
|
||||||
|
1. Register the app
|
||||||
|
1. Download the config file - GoogleInfo.plist (this will need to be included in the ntfy-ios repository / XCode)
|
||||||
|
1. Generate a new service account private key for the ntfy server
|
||||||
|
1. Go to "Project settings" > "Service accounts"
|
||||||
|
1. Click "Generate new private key" to generate and download a private key to use for sending messages via the ntfy server
|
||||||
|
|
||||||
|
### ntfy server
|
||||||
|
Note that the ntfy server is not officially supported on macOS. It should, however, be able to run on macOS using these
|
||||||
|
steps:
|
||||||
|
|
||||||
|
1. If not already made, make the `/etc/ntfy/` directory and move the service account private key to that folder
|
||||||
|
1. Copy the `server/server.yml` file from the ntfy repository to `/etc/ntfy/`
|
||||||
|
1. Modify the `/etc/ntfy/server.yml` file `firebase-key-file` value to the path of the private key
|
||||||
|
1. Install go: `brew install go`
|
||||||
|
1. In the ntfy repository, run `make cli-darwin-server`.
|
||||||
|
|
||||||
|
### XCode setup
|
||||||
|
|
||||||
|
1. Follow step 4 of [https://firebase.google.com/docs/ios/setup](Add Firebase to your Apple project) to install the
|
||||||
|
`firebase-ios-sdk` in XCode, if it's not already present - you can select any packages in addition to Firebase Core / Firebase Messaging
|
||||||
|
1. Similarly, install the SQLite.swift package dependency in XCode
|
||||||
|
1. When running the debug build, ensure XCode is pointed to the connected iOS device - registering for push notifications does not work in the iOS simulators
|
||||||
|
|
||||||
|
### PLIST config
|
||||||
|
To have instant notifications/better notification delivery when using firebase, you will need to add the
|
||||||
|
`GoogleService-Info.plist` file to your project. Here's how to do that:
|
||||||
|
|
||||||
|
1. In XCode, find the NTFY app target. **Not** the NSE app target.
|
||||||
|
1. Find the Asset/ folder in the project navigator
|
||||||
|
1. Drag the `GoogleService-Info.plist` file into the Asset/ folder that you get from the firebase console. It can be
|
||||||
|
found in the "Project settings" > "General" > "Your apps" with a button labled "GoogleService-Info.plist"
|
||||||
|
|
||||||
|
After that, you should be all set!
|
||||||
|
|
|
@ -2,13 +2,18 @@
|
||||||
Binaries for all releases can be found on the GitHub releases pages for the [ntfy server](https://github.com/binwiederhier/ntfy/releases)
|
Binaries for all releases can be found on the GitHub releases pages for the [ntfy server](https://github.com/binwiederhier/ntfy/releases)
|
||||||
and the [ntfy Android app](https://github.com/binwiederhier/ntfy-android/releases).
|
and the [ntfy Android app](https://github.com/binwiederhier/ntfy-android/releases).
|
||||||
|
|
||||||
## ntfy server v1.31.0 (UNRELEASED)
|
## ntfy server v2.0.0 (UNRELEASED)
|
||||||
Released XXXX
|
|
||||||
|
|
||||||
This is the biggest ntfy server release I've ever done. This release adds the ability to sign-up for accounts, log-in
|
This is the biggest ntfy server release I've ever done. This release adds the ability to sign-up for accounts, log-in
|
||||||
via the web app, synchronize accounts between devices (web only for now), introduces user access tokens, user tiers,
|
via the web app, synchronize accounts between devices (web only for now), introduces user access tokens, user tiers,
|
||||||
and Stripe payments integration to support paid tiers (don't worry, [everything will stay open source](https://ntfy.sh/docs/faq/)).
|
and Stripe payments integration to support paid tiers (don't worry, [everything will stay open source](https://ntfy.sh/docs/faq/)).
|
||||||
|
|
||||||
|
**Bug fixes + maintenance:**
|
||||||
|
|
||||||
|
* `OPTIONS` method calls are not serviced when the UI is disabled ([#598](https://github.com/binwiederhier/ntfy/issues/598), thanks to [@enticedwanderer](https://github.com/enticedwanderer) for reporting)
|
||||||
|
|
||||||
|
## ntfy server v1.31.0 (UNRELEASED)
|
||||||
|
|
||||||
**Features:**
|
**Features:**
|
||||||
|
|
||||||
* ⭐ User account signup, login, topic reservations, access tokens, tiers etc. ⭐ ([#522](https://github.com/binwiederhier/ntfy/issues/522))
|
* ⭐ User account signup, login, topic reservations, access tokens, tiers etc. ⭐ ([#522](https://github.com/binwiederhier/ntfy/issues/522))
|
||||||
|
@ -19,7 +24,7 @@ and Stripe payments integration to support paid tiers (don't worry, [everything
|
||||||
|
|
||||||
* Fix `chown` issues with RHEL-like based systems ([#566](https://github.com/binwiederhier/ntfy/issues/566)/[#565](https://github.com/binwiederhier/ntfy/pull/565), thanks to [@danieldemus](https://github.com/danieldemus))
|
* Fix `chown` issues with RHEL-like based systems ([#566](https://github.com/binwiederhier/ntfy/issues/566)/[#565](https://github.com/binwiederhier/ntfy/pull/565), thanks to [@danieldemus](https://github.com/danieldemus))
|
||||||
* Removed `upx` (binary packing) for all builds due to false virus warnings ([#576](https://github.com/binwiederhier/ntfy/issues/576), thanks to [@shawnhwei](https://github.com/shawnhwei) for reporting)
|
* Removed `upx` (binary packing) for all builds due to false virus warnings ([#576](https://github.com/binwiederhier/ntfy/issues/576), thanks to [@shawnhwei](https://github.com/shawnhwei) for reporting)
|
||||||
* `OPTIONS` method calls are not serviced when the UI is disabled ([#598](https://github.com/binwiederhier/ntfy/issues/598), thanks to [@enticedwanderer](https://github.com/enticedwanderer) for reporting)
|
* Upgraded `go-smtp` library and tests to v0.16.0 ([#569](https://github.com/binwiederhier/ntfy/issues/569))
|
||||||
|
|
||||||
**Documentation:**
|
**Documentation:**
|
||||||
|
|
||||||
|
@ -27,6 +32,7 @@ and Stripe payments integration to support paid tiers (don't worry, [everything
|
||||||
* Small wording change for `client.yml` ([#562](https://github.com/binwiederhier/ntfy/pull/562), thanks to [@fleopaulD](https://github.com/fleopaulD))
|
* Small wording change for `client.yml` ([#562](https://github.com/binwiederhier/ntfy/pull/562), thanks to [@fleopaulD](https://github.com/fleopaulD))
|
||||||
* Fix K8s install docs ([#582](https://github.com/binwiederhier/ntfy/pull/582), thanks to [@Remedan](https://github.com/Remedan))
|
* Fix K8s install docs ([#582](https://github.com/binwiederhier/ntfy/pull/582), thanks to [@Remedan](https://github.com/Remedan))
|
||||||
* Updated Jellyseer docs ([#604](https://github.com/binwiederhier/ntfy/pull/604), thanks to [@Y0ngg4n](https://github.com/Y0ngg4n))
|
* Updated Jellyseer docs ([#604](https://github.com/binwiederhier/ntfy/pull/604), thanks to [@Y0ngg4n](https://github.com/Y0ngg4n))
|
||||||
|
* Updated iOS developer docs ([#605](https://github.com/binwiederhier/ntfy/pull/605), thanks to [@SticksDev](https://github.com/SticksDev))
|
||||||
|
|
||||||
**Additional languages:**
|
**Additional languages:**
|
||||||
|
|
||||||
|
|
36
go.mod
36
go.mod
|
@ -4,22 +4,22 @@ go 1.18
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go/firestore v1.9.0 // indirect
|
cloud.google.com/go/firestore v1.9.0 // indirect
|
||||||
cloud.google.com/go/storage v1.28.1 // indirect
|
cloud.google.com/go/storage v1.29.0 // indirect
|
||||||
github.com/BurntSushi/toml v1.2.1 // indirect
|
github.com/BurntSushi/toml v1.2.1 // indirect
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||||
github.com/emersion/go-smtp v0.15.0
|
github.com/emersion/go-smtp v0.16.0
|
||||||
github.com/gabriel-vasile/mimetype v1.4.1
|
github.com/gabriel-vasile/mimetype v1.4.1
|
||||||
github.com/gorilla/websocket v1.5.0
|
github.com/gorilla/websocket v1.5.0
|
||||||
github.com/mattn/go-sqlite3 v1.14.16
|
github.com/mattn/go-sqlite3 v1.14.16
|
||||||
github.com/olebedev/when v0.0.0-20221205223600-4d190b02b8d8
|
github.com/olebedev/when v0.0.0-20221205223600-4d190b02b8d8
|
||||||
github.com/stretchr/testify v1.8.1
|
github.com/stretchr/testify v1.8.1
|
||||||
github.com/urfave/cli/v2 v2.23.7
|
github.com/urfave/cli/v2 v2.24.3
|
||||||
golang.org/x/crypto v0.4.0
|
golang.org/x/crypto v0.6.0
|
||||||
golang.org/x/oauth2 v0.3.0 // indirect
|
golang.org/x/oauth2 v0.5.0 // indirect
|
||||||
golang.org/x/sync v0.1.0
|
golang.org/x/sync v0.1.0
|
||||||
golang.org/x/term v0.3.0
|
golang.org/x/term v0.5.0
|
||||||
golang.org/x/time v0.3.0
|
golang.org/x/time v0.3.0
|
||||||
google.golang.org/api v0.105.0
|
google.golang.org/api v0.110.0
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -27,15 +27,15 @@ require github.com/pkg/errors v0.9.1 // indirect
|
||||||
|
|
||||||
require (
|
require (
|
||||||
firebase.google.com/go/v4 v4.10.0
|
firebase.google.com/go/v4 v4.10.0
|
||||||
github.com/stripe/stripe-go/v74 v74.5.0
|
github.com/stripe/stripe-go/v74 v74.7.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go v0.107.0 // indirect
|
cloud.google.com/go v0.109.0 // indirect
|
||||||
cloud.google.com/go/compute v1.14.0 // indirect
|
cloud.google.com/go/compute v1.18.0 // indirect
|
||||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||||
cloud.google.com/go/iam v0.9.0 // indirect
|
cloud.google.com/go/iam v0.10.0 // indirect
|
||||||
cloud.google.com/go/longrunning v0.3.0 // indirect
|
cloud.google.com/go/longrunning v0.4.1 // indirect
|
||||||
github.com/AlekSi/pointer v1.2.0 // indirect
|
github.com/AlekSi/pointer v1.2.0 // indirect
|
||||||
github.com/MicahParks/keyfunc v1.9.0 // indirect
|
github.com/MicahParks/keyfunc v1.9.0 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
@ -45,21 +45,21 @@ require (
|
||||||
github.com/golang/protobuf v1.5.2 // indirect
|
github.com/golang/protobuf v1.5.2 // indirect
|
||||||
github.com/google/go-cmp v0.5.9 // indirect
|
github.com/google/go-cmp v0.5.9 // indirect
|
||||||
github.com/google/uuid v1.3.0 // indirect
|
github.com/google/uuid v1.3.0 // indirect
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.2.1 // indirect
|
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
|
||||||
github.com/googleapis/gax-go/v2 v2.7.0 // indirect
|
github.com/googleapis/gax-go/v2 v2.7.0 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
github.com/stretchr/objx v0.5.0 // indirect
|
github.com/stretchr/objx v0.5.0 // indirect
|
||||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||||
go.opencensus.io v0.24.0 // indirect
|
go.opencensus.io v0.24.0 // indirect
|
||||||
golang.org/x/net v0.4.0 // indirect
|
golang.org/x/net v0.7.0 // indirect
|
||||||
golang.org/x/sys v0.3.0 // indirect
|
golang.org/x/sys v0.5.0 // indirect
|
||||||
golang.org/x/text v0.5.0 // indirect
|
golang.org/x/text v0.7.0 // indirect
|
||||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||||
google.golang.org/appengine v1.6.7 // indirect
|
google.golang.org/appengine v1.6.7 // indirect
|
||||||
google.golang.org/appengine/v2 v2.0.2 // indirect
|
google.golang.org/appengine/v2 v2.0.2 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef // indirect
|
google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc // indirect
|
||||||
google.golang.org/grpc v1.51.0 // indirect
|
google.golang.org/grpc v1.53.0 // indirect
|
||||||
google.golang.org/protobuf v1.28.1 // indirect
|
google.golang.org/protobuf v1.28.1 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
72
go.sum
72
go.sum
|
@ -1,18 +1,18 @@
|
||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
cloud.google.com/go v0.107.0 h1:qkj22L7bgkl6vIeZDlOY2po43Mx/TIa2Wsa7VR+PEww=
|
cloud.google.com/go v0.109.0 h1:38CZoKGlCnPZjGdyj0ZfpoGae0/wgNfy5F0byyxg0Gk=
|
||||||
cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I=
|
cloud.google.com/go v0.109.0/go.mod h1:2sYycXt75t/CSB5R9M2wPU1tJmire7AQZTPtITcGBVE=
|
||||||
cloud.google.com/go/compute v1.14.0 h1:hfm2+FfxVmnRlh6LpB7cg1ZNU+5edAHmW679JePztk0=
|
cloud.google.com/go/compute v1.18.0 h1:FEigFqoDbys2cvFkZ9Fjq4gnHBP55anJ0yQyau2f9oY=
|
||||||
cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo=
|
cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs=
|
||||||
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
|
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
|
||||||
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
|
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
|
||||||
cloud.google.com/go/firestore v1.9.0 h1:IBlRyxgGySXu5VuW0RgGFlTtLukSnNkpDiEOMkQkmpA=
|
cloud.google.com/go/firestore v1.9.0 h1:IBlRyxgGySXu5VuW0RgGFlTtLukSnNkpDiEOMkQkmpA=
|
||||||
cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE=
|
cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE=
|
||||||
cloud.google.com/go/iam v0.9.0 h1:bK6Or6mxhuL8lnj1i9j0yMo2wE/IeTO2cWlfUrf/TZs=
|
cloud.google.com/go/iam v0.10.0 h1:fpP/gByFs6US1ma53v7VxhvbJpO2Aapng6wabJ99MuI=
|
||||||
cloud.google.com/go/iam v0.9.0/go.mod h1:nXAECrMt2qHpF6RZUZseteD6QyanL68reN4OXPw0UWM=
|
cloud.google.com/go/iam v0.10.0/go.mod h1:nXAECrMt2qHpF6RZUZseteD6QyanL68reN4OXPw0UWM=
|
||||||
cloud.google.com/go/longrunning v0.3.0 h1:NjljC+FYPV3uh5/OwWT6pVU+doBqMg2x/rZlE+CamDs=
|
cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM=
|
||||||
cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc=
|
cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo=
|
||||||
cloud.google.com/go/storage v1.28.1 h1:F5QDG5ChchaAVQhINh24U99OWHURqrW8OmQcGKXcbgI=
|
cloud.google.com/go/storage v1.29.0 h1:6weCgzRvMg7lzuUurI4697AqIRPU1SvzHhynwpW31jI=
|
||||||
cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y=
|
cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4=
|
||||||
firebase.google.com/go/v4 v4.10.0 h1:dgK/8uwfJbzc5LZK/GyRRfIkZEDObN9q0kgEXsjlXN4=
|
firebase.google.com/go/v4 v4.10.0 h1:dgK/8uwfJbzc5LZK/GyRRfIkZEDObN9q0kgEXsjlXN4=
|
||||||
firebase.google.com/go/v4 v4.10.0/go.mod h1:m0gLwPY9fxKggizzglgCNWOGnFnVPifLpqZzo5u3e/A=
|
firebase.google.com/go/v4 v4.10.0/go.mod h1:m0gLwPY9fxKggizzglgCNWOGnFnVPifLpqZzo5u3e/A=
|
||||||
github.com/AlekSi/pointer v1.2.0 h1:glcy/gc4h8HnG2Z3ZECSzZ1IX1x2JxRVuDzaJwQE0+w=
|
github.com/AlekSi/pointer v1.2.0 h1:glcy/gc4h8HnG2Z3ZECSzZ1IX1x2JxRVuDzaJwQE0+w=
|
||||||
|
@ -33,8 +33,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
||||||
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
|
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
|
||||||
github.com/emersion/go-sasl v0.0.0-20220912192320-0145f2c60ead h1:fI1Jck0vUrXT8bnphprS1EoVRe2Q5CKCX8iDlpqjQ/Y=
|
github.com/emersion/go-sasl v0.0.0-20220912192320-0145f2c60ead h1:fI1Jck0vUrXT8bnphprS1EoVRe2Q5CKCX8iDlpqjQ/Y=
|
||||||
github.com/emersion/go-sasl v0.0.0-20220912192320-0145f2c60ead/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
|
github.com/emersion/go-sasl v0.0.0-20220912192320-0145f2c60ead/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
|
||||||
github.com/emersion/go-smtp v0.15.0 h1:3+hMGMGrqP/lqd7qoxZc1hTU8LY8gHV9RFGWlqSDmP8=
|
github.com/emersion/go-smtp v0.16.0 h1:eB9CY9527WdEZSs5sWisTmilDX7gG+Q/2IdRcmubpa8=
|
||||||
github.com/emersion/go-smtp v0.15.0/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ=
|
github.com/emersion/go-smtp v0.16.0/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
|
@ -75,8 +75,8 @@ github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1V
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.2.1 h1:RY7tHKZcRlk788d5WSo/e83gOyyy742E8GSs771ySpg=
|
github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k=
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
|
github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
|
||||||
github.com/googleapis/gax-go/v2 v2.7.0 h1:IcsPKeInNvYi7eqSaDjiZqDDKu5rsmunY0Y1YupQSSQ=
|
github.com/googleapis/gax-go/v2 v2.7.0 h1:IcsPKeInNvYi7eqSaDjiZqDDKu5rsmunY0Y1YupQSSQ=
|
||||||
github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8=
|
github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8=
|
||||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||||
|
@ -101,18 +101,18 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/stripe/stripe-go/v74 v74.5.0 h1:YyqTvVQdS34KYGCfVB87EMn9eDV3FCFkSwfdOQhiVL4=
|
github.com/stripe/stripe-go/v74 v74.7.0 h1:KHlyslQj9YOv62b1sycQ31LFj7KlqR+seHsSowAWrjc=
|
||||||
github.com/stripe/stripe-go/v74 v74.5.0/go.mod h1:5PoXNp30AJ3tGq57ZcFuaMylzNi8KpwlrYAFmO1fHZw=
|
github.com/stripe/stripe-go/v74 v74.7.0/go.mod h1:5PoXNp30AJ3tGq57ZcFuaMylzNi8KpwlrYAFmO1fHZw=
|
||||||
github.com/urfave/cli/v2 v2.23.7 h1:YHDQ46s3VghFHFf1DdF+Sh7H4RqhcM+t0TmZRJx4oJY=
|
github.com/urfave/cli/v2 v2.24.3 h1:7Q1w8VN8yE0MJEHP06bv89PjYsN4IHWED2s1v/Zlfm0=
|
||||||
github.com/urfave/cli/v2 v2.23.7/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
|
github.com/urfave/cli/v2 v2.24.3/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
|
||||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
|
||||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
||||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.4.0 h1:UVQgzMY87xqpKNgb+kDsll2Igd33HszWHFLmpaRMq/8=
|
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
|
||||||
golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80=
|
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
|
@ -127,11 +127,11 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/
|
||||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.0.0-20220708220712-1185a9018129/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220708220712-1185a9018129/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU=
|
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||||
golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.3.0 h1:6l90koy8/LaBLmLu8jpHeHexzMwEita0zFfYlggy2F8=
|
golang.org/x/oauth2 v0.5.0 h1:HuArIo48skDwlrvM3sEdHXElYslAMsf3KwRkkW4MC4s=
|
||||||
golang.org/x/oauth2 v0.3.0/go.mod h1:rQrIauxkUhJ6CuwEXwymO2/eh4xz2ZWF1nBkcxS+tGk=
|
golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
@ -144,17 +144,17 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
|
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI=
|
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
|
||||||
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
|
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
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/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
@ -165,8 +165,8 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
|
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
|
||||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
||||||
google.golang.org/api v0.105.0 h1:t6P9Jj+6XTn4U9I2wycQai6Q/Kz7iOT+QzjJ3G2V4x8=
|
google.golang.org/api v0.110.0 h1:l+rh0KYUooe9JGbGVx71tbFo4SMbMTXK3I3ia2QSEeU=
|
||||||
google.golang.org/api v0.105.0/go.mod h1:qh7eD5FJks5+BcE+cjBIm6Gz8vioK7EHvnlniqXBnqI=
|
google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||||
|
@ -176,15 +176,15 @@ google.golang.org/appengine/v2 v2.0.2/go.mod h1:PkgRUWz4o1XOvbqtWTkBtCitEJ5Tp4Ho
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||||
google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef h1:uQ2vjV/sHTsWSqdKeLqmwitzgvjMl7o4IdtHwUDXSJY=
|
google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc h1:ijGwO+0vL2hJt5gaygqP2j6PfflOBrRot0IczKbmtio=
|
||||||
google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
|
google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||||
google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U=
|
google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc=
|
||||||
google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww=
|
google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
|
|
|
@ -37,13 +37,12 @@ func logvm(v *visitor, m *message) *log.Event {
|
||||||
}
|
}
|
||||||
|
|
||||||
// logem creates a new log event with email fields
|
// logem creates a new log event with email fields
|
||||||
func logem(state *smtp.ConnectionState) *log.Event {
|
func logem(smtpConn *smtp.Conn) *log.Event {
|
||||||
return log.
|
ev := log.Tag(tagSMTP).Field("smtp_hostname", smtpConn.Hostname())
|
||||||
Tag(tagSMTP).
|
if smtpConn.Conn() != nil {
|
||||||
Fields(log.Context{
|
ev.Field("smtp_remote_addr", smtpConn.Conn().RemoteAddr().String())
|
||||||
"smtp_hostname": state.Hostname,
|
}
|
||||||
"smtp_remote_addr": state.RemoteAddr.String(),
|
return ev
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func httpContext(r *http.Request) log.Context {
|
func httpContext(r *http.Request) log.Context {
|
||||||
|
|
|
@ -33,6 +33,9 @@ type smtpBackend struct {
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ smtp.Backend = (*smtpBackend)(nil)
|
||||||
|
var _ smtp.Session = (*smtpSession)(nil)
|
||||||
|
|
||||||
func newMailBackend(conf *Config, handler func(http.ResponseWriter, *http.Request)) *smtpBackend {
|
func newMailBackend(conf *Config, handler func(http.ResponseWriter, *http.Request)) *smtpBackend {
|
||||||
return &smtpBackend{
|
return &smtpBackend{
|
||||||
config: conf,
|
config: conf,
|
||||||
|
@ -40,14 +43,9 @@ func newMailBackend(conf *Config, handler func(http.ResponseWriter, *http.Reques
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *smtpBackend) Login(state *smtp.ConnectionState, username, _ string) (smtp.Session, error) {
|
func (b *smtpBackend) NewSession(conn *smtp.Conn) (smtp.Session, error) {
|
||||||
logem(state).Debug("Incoming mail, login with user %s", username)
|
logem(conn).Debug("Incoming mail")
|
||||||
return &smtpSession{backend: b, state: state}, nil
|
return &smtpSession{backend: b, conn: conn}, nil
|
||||||
}
|
|
||||||
|
|
||||||
func (b *smtpBackend) AnonymousLogin(state *smtp.ConnectionState) (smtp.Session, error) {
|
|
||||||
logem(state).Debug("Incoming mail, anonymous login")
|
|
||||||
return &smtpSession{backend: b, state: state}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *smtpBackend) Counts() (total int64, success int64, failure int64) {
|
func (b *smtpBackend) Counts() (total int64, success int64, failure int64) {
|
||||||
|
@ -59,23 +57,23 @@ func (b *smtpBackend) Counts() (total int64, success int64, failure int64) {
|
||||||
// smtpSession is returned after EHLO.
|
// smtpSession is returned after EHLO.
|
||||||
type smtpSession struct {
|
type smtpSession struct {
|
||||||
backend *smtpBackend
|
backend *smtpBackend
|
||||||
state *smtp.ConnectionState
|
conn *smtp.Conn
|
||||||
topic string
|
topic string
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *smtpSession) AuthPlain(username, password string) error {
|
func (s *smtpSession) AuthPlain(username, _ string) error {
|
||||||
logem(s.state).Debug("AUTH PLAIN (with username %s)", username)
|
logem(s.conn).Field("smtp_username", username).Debug("AUTH PLAIN (with username %s)", username)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *smtpSession) Mail(from string, opts smtp.MailOptions) error {
|
func (s *smtpSession) Mail(from string, opts *smtp.MailOptions) error {
|
||||||
logem(s.state).Debug("MAIL FROM: %s (with options: %#v)", from, opts)
|
logem(s.conn).Field("smtp_mail_from", from).Debug("MAIL FROM: %s", from)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *smtpSession) Rcpt(to string) error {
|
func (s *smtpSession) Rcpt(to string) error {
|
||||||
logem(s.state).Debug("RCPT TO: %s", to)
|
logem(s.conn).Field("smtp_rcpt_to", to).Debug("RCPT TO: %s", to)
|
||||||
return s.withFailCount(func() error {
|
return s.withFailCount(func() error {
|
||||||
conf := s.backend.config
|
conf := s.backend.config
|
||||||
addressList, err := mail.ParseAddressList(to)
|
addressList, err := mail.ParseAddressList(to)
|
||||||
|
@ -112,11 +110,11 @@ func (s *smtpSession) Data(r io.Reader) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ev := logem(s.state).Tag(tagSMTP)
|
ev := logem(s.conn)
|
||||||
if ev.IsTrace() {
|
if ev.IsTrace() {
|
||||||
ev.Field("smtp_data", string(b)).Trace("DATA")
|
ev.Field("smtp_data", string(b)).Trace("DATA")
|
||||||
} else if ev.IsDebug() {
|
} else if ev.IsDebug() {
|
||||||
ev.Debug("DATA: %d byte(s)", len(b))
|
ev.Field("smtp_data_len", len(b)).Debug("DATA")
|
||||||
}
|
}
|
||||||
msg, err := mail.ReadMessage(bytes.NewReader(b))
|
msg, err := mail.ReadMessage(bytes.NewReader(b))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -156,9 +154,9 @@ func (s *smtpSession) Data(r io.Reader) error {
|
||||||
|
|
||||||
func (s *smtpSession) publishMessage(m *message) error {
|
func (s *smtpSession) publishMessage(m *message) error {
|
||||||
// Extract remote address (for rate limiting)
|
// Extract remote address (for rate limiting)
|
||||||
remoteAddr, _, err := net.SplitHostPort(s.state.RemoteAddr.String())
|
remoteAddr, _, err := net.SplitHostPort(s.conn.Conn().RemoteAddr().String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
remoteAddr = s.state.RemoteAddr.String()
|
remoteAddr = s.conn.Conn().RemoteAddr().String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call HTTP handler with fake HTTP request
|
// Call HTTP handler with fake HTTP request
|
||||||
|
@ -198,7 +196,7 @@ func (s *smtpSession) withFailCount(fn func() error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Almost all of these errors are parse errors, and user input errors.
|
// Almost all of these errors are parse errors, and user input errors.
|
||||||
// We do not want to spam the log with WARN messages.
|
// We do not want to spam the log with WARN messages.
|
||||||
logem(s.state).Err(err).Debug("Incoming mail error")
|
logem(s.conn).Err(err).Debug("Incoming mail error")
|
||||||
s.backend.failure++
|
s.backend.failure++
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -1,16 +1,23 @@
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"github.com/emersion/go-smtp"
|
"github.com/emersion/go-smtp"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSmtpBackend_Multipart(t *testing.T) {
|
func TestSmtpBackend_Multipart(t *testing.T) {
|
||||||
email := `MIME-Version: 1.0
|
email := `EHLO example.com
|
||||||
|
MAIL FROM: phil@example.com
|
||||||
|
RCPT TO: ntfy-mytopic@ntfy.sh
|
||||||
|
DATA
|
||||||
|
MIME-Version: 1.0
|
||||||
Date: Tue, 28 Dec 2021 00:30:10 +0100
|
Date: Tue, 28 Dec 2021 00:30:10 +0100
|
||||||
Message-ID: <CAAvm79YP0C=Rt1N=KWmSUBB87KK2rRChmdzKqF1vCwMEUiVzLQ@mail.gmail.com>
|
Message-ID: <CAAvm79YP0C=Rt1N=KWmSUBB87KK2rRChmdzKqF1vCwMEUiVzLQ@mail.gmail.com>
|
||||||
Subject: and one more
|
Subject: and one more
|
||||||
|
@ -28,20 +35,25 @@ Content-Type: text/html; charset="UTF-8"
|
||||||
|
|
||||||
<div dir="ltr">what's up<br clear="all"><div><br></div></div>
|
<div dir="ltr">what's up<br clear="all"><div><br></div></div>
|
||||||
|
|
||||||
--000000000000f3320b05d42915c9--`
|
--000000000000f3320b05d42915c9--
|
||||||
_, backend := newTestBackend(t, func(w http.ResponseWriter, r *http.Request) {
|
.
|
||||||
|
`
|
||||||
|
s, c, _, scanner := newTestSMTPServer(t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
require.Equal(t, "/mytopic", r.URL.Path)
|
require.Equal(t, "/mytopic", r.URL.Path)
|
||||||
require.Equal(t, "and one more", r.Header.Get("Title"))
|
require.Equal(t, "and one more", r.Header.Get("Title"))
|
||||||
require.Equal(t, "what's up", readAll(t, r.Body))
|
require.Equal(t, "what's up", readAll(t, r.Body))
|
||||||
})
|
})
|
||||||
session, _ := backend.AnonymousLogin(fakeConnState(t, "1.2.3.4"))
|
defer s.Close()
|
||||||
require.Nil(t, session.Mail("phil@example.com", smtp.MailOptions{}))
|
defer c.Close()
|
||||||
require.Nil(t, session.Rcpt("ntfy-mytopic@ntfy.sh"))
|
writeAndReadUntilLine(t, email, c, scanner, "250 2.0.0 OK: queued")
|
||||||
require.Nil(t, session.Data(strings.NewReader(email)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSmtpBackend_MultipartNoBody(t *testing.T) {
|
func TestSmtpBackend_MultipartNoBody(t *testing.T) {
|
||||||
email := `MIME-Version: 1.0
|
email := `EHLO example.com
|
||||||
|
MAIL FROM: phil@example.com
|
||||||
|
RCPT TO: ntfy-emailtest@ntfy.sh
|
||||||
|
DATA
|
||||||
|
MIME-Version: 1.0
|
||||||
Date: Tue, 28 Dec 2021 01:33:34 +0100
|
Date: Tue, 28 Dec 2021 01:33:34 +0100
|
||||||
Message-ID: <CAAvm7ABCDsi9vsuu0WTRXzZQBC8dXrDOLT8iCWdqrsmg@mail.gmail.com>
|
Message-ID: <CAAvm7ABCDsi9vsuu0WTRXzZQBC8dXrDOLT8iCWdqrsmg@mail.gmail.com>
|
||||||
Subject: This email has a subject but no body
|
Subject: This email has a subject but no body
|
||||||
|
@ -59,20 +71,25 @@ Content-Type: text/html; charset="UTF-8"
|
||||||
|
|
||||||
<div dir="ltr"><br></div>
|
<div dir="ltr"><br></div>
|
||||||
|
|
||||||
--000000000000bcf4a405d429f8d4--`
|
--000000000000bcf4a405d429f8d4--
|
||||||
_, backend := newTestBackend(t, func(w http.ResponseWriter, r *http.Request) {
|
.
|
||||||
|
`
|
||||||
|
s, c, _, scanner := newTestSMTPServer(t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
require.Equal(t, "/emailtest", r.URL.Path)
|
require.Equal(t, "/emailtest", r.URL.Path)
|
||||||
require.Equal(t, "", r.Header.Get("Title")) // We flipped message and body
|
require.Equal(t, "", r.Header.Get("Title")) // We flipped message and body
|
||||||
require.Equal(t, "This email has a subject but no body", readAll(t, r.Body))
|
require.Equal(t, "This email has a subject but no body", readAll(t, r.Body))
|
||||||
})
|
})
|
||||||
session, _ := backend.AnonymousLogin(fakeConnState(t, "1.2.3.4"))
|
defer s.Close()
|
||||||
require.Nil(t, session.Mail("phil@example.com", smtp.MailOptions{}))
|
defer c.Close()
|
||||||
require.Nil(t, session.Rcpt("ntfy-emailtest@ntfy.sh"))
|
writeAndReadUntilLine(t, email, c, scanner, "250 2.0.0 OK: queued")
|
||||||
require.Nil(t, session.Data(strings.NewReader(email)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSmtpBackend_Plaintext(t *testing.T) {
|
func TestSmtpBackend_Plaintext(t *testing.T) {
|
||||||
email := `Date: Tue, 28 Dec 2021 00:30:10 +0100
|
email := `EHLO example.com
|
||||||
|
MAIL FROM: phil@example.com
|
||||||
|
RCPT TO: mytopic@ntfy.sh
|
||||||
|
DATA
|
||||||
|
Date: Tue, 28 Dec 2021 00:30:10 +0100
|
||||||
Message-ID: <CAAvm79YP0C=Rt1N=KWmSUBB87KK2rRChmdzKqF1vCwMEUiVzLQ@mail.gmail.com>
|
Message-ID: <CAAvm79YP0C=Rt1N=KWmSUBB87KK2rRChmdzKqF1vCwMEUiVzLQ@mail.gmail.com>
|
||||||
Subject: and one more
|
Subject: and one more
|
||||||
From: Phil <phil@example.com>
|
From: Phil <phil@example.com>
|
||||||
|
@ -80,56 +97,68 @@ To: mytopic@ntfy.sh
|
||||||
Content-Type: text/plain; charset="UTF-8"
|
Content-Type: text/plain; charset="UTF-8"
|
||||||
|
|
||||||
what's up
|
what's up
|
||||||
|
.
|
||||||
`
|
`
|
||||||
conf, backend := newTestBackend(t, func(w http.ResponseWriter, r *http.Request) {
|
s, c, conf, scanner := newTestSMTPServer(t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
require.Equal(t, "/mytopic", r.URL.Path)
|
require.Equal(t, "/mytopic", r.URL.Path)
|
||||||
require.Equal(t, "and one more", r.Header.Get("Title"))
|
require.Equal(t, "and one more", r.Header.Get("Title"))
|
||||||
require.Equal(t, "what's up", readAll(t, r.Body))
|
require.Equal(t, "what's up", readAll(t, r.Body))
|
||||||
})
|
})
|
||||||
conf.SMTPServerAddrPrefix = ""
|
conf.SMTPServerAddrPrefix = ""
|
||||||
session, _ := backend.AnonymousLogin(fakeConnState(t, "1.2.3.4"))
|
defer s.Close()
|
||||||
require.Nil(t, session.Mail("phil@example.com", smtp.MailOptions{}))
|
defer c.Close()
|
||||||
require.Nil(t, session.Rcpt("mytopic@ntfy.sh"))
|
writeAndReadUntilLine(t, email, c, scanner, "250 2.0.0 OK: queued")
|
||||||
require.Nil(t, session.Data(strings.NewReader(email)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSmtpBackend_Plaintext_No_ContentType(t *testing.T) {
|
func TestSmtpBackend_Plaintext_No_ContentType(t *testing.T) {
|
||||||
email := `Subject: Very short mail
|
email := `EHLO example.com
|
||||||
|
MAIL FROM: phil@example.com
|
||||||
|
RCPT TO: mytopic@ntfy.sh
|
||||||
|
DATA
|
||||||
|
Subject: Very short mail
|
||||||
|
|
||||||
what's up
|
what's up
|
||||||
|
.
|
||||||
`
|
`
|
||||||
conf, backend := newTestBackend(t, func(w http.ResponseWriter, r *http.Request) {
|
s, c, conf, scanner := newTestSMTPServer(t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
require.Equal(t, "/mytopic", r.URL.Path)
|
require.Equal(t, "/mytopic", r.URL.Path)
|
||||||
require.Equal(t, "Very short mail", r.Header.Get("Title"))
|
require.Equal(t, "Very short mail", r.Header.Get("Title"))
|
||||||
require.Equal(t, "what's up", readAll(t, r.Body))
|
require.Equal(t, "what's up", readAll(t, r.Body))
|
||||||
})
|
})
|
||||||
conf.SMTPServerAddrPrefix = ""
|
conf.SMTPServerAddrPrefix = ""
|
||||||
session, _ := backend.AnonymousLogin(fakeConnState(t, "1.2.3.4"))
|
defer s.Close()
|
||||||
require.Nil(t, session.Mail("phil@example.com", smtp.MailOptions{}))
|
defer c.Close()
|
||||||
require.Nil(t, session.Rcpt("mytopic@ntfy.sh"))
|
writeAndReadUntilLine(t, email, c, scanner, "250 2.0.0 OK: queued")
|
||||||
require.Nil(t, session.Data(strings.NewReader(email)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSmtpBackend_Plaintext_EncodedSubject(t *testing.T) {
|
func TestSmtpBackend_Plaintext_EncodedSubject(t *testing.T) {
|
||||||
email := `Date: Tue, 28 Dec 2021 00:30:10 +0100
|
email := `EHLO example.com
|
||||||
|
MAIL FROM: phil@example.com
|
||||||
|
RCPT TO: ntfy-mytopic@ntfy.sh
|
||||||
|
DATA
|
||||||
|
Date: Tue, 28 Dec 2021 00:30:10 +0100
|
||||||
Subject: =?UTF-8?B?VGhyZWUgc2FudGFzIPCfjoXwn46F8J+OhQ==?=
|
Subject: =?UTF-8?B?VGhyZWUgc2FudGFzIPCfjoXwn46F8J+OhQ==?=
|
||||||
From: Phil <phil@example.com>
|
From: Phil <phil@example.com>
|
||||||
To: ntfy-mytopic@ntfy.sh
|
To: ntfy-mytopic@ntfy.sh
|
||||||
Content-Type: text/plain; charset="UTF-8"
|
Content-Type: text/plain; charset="UTF-8"
|
||||||
|
|
||||||
what's up
|
what's up
|
||||||
|
.
|
||||||
`
|
`
|
||||||
_, backend := newTestBackend(t, func(w http.ResponseWriter, r *http.Request) {
|
s, c, _, scanner := newTestSMTPServer(t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
require.Equal(t, "Three santas 🎅🎅🎅", r.Header.Get("Title"))
|
require.Equal(t, "Three santas 🎅🎅🎅", r.Header.Get("Title"))
|
||||||
})
|
})
|
||||||
session, _ := backend.AnonymousLogin(fakeConnState(t, "1.2.3.4"))
|
defer s.Close()
|
||||||
require.Nil(t, session.Mail("phil@example.com", smtp.MailOptions{}))
|
defer c.Close()
|
||||||
require.Nil(t, session.Rcpt("ntfy-mytopic@ntfy.sh"))
|
writeAndReadUntilLine(t, email, c, scanner, "250 2.0.0 OK: queued")
|
||||||
require.Nil(t, session.Data(strings.NewReader(email)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSmtpBackend_Plaintext_TooLongTruncate(t *testing.T) {
|
func TestSmtpBackend_Plaintext_TooLongTruncate(t *testing.T) {
|
||||||
email := `Date: Tue, 28 Dec 2021 00:30:10 +0100
|
email := `EHLO example.com
|
||||||
|
MAIL FROM: phil@example.com
|
||||||
|
RCPT TO: mytopic@ntfy.sh
|
||||||
|
DATA
|
||||||
|
Date: Tue, 28 Dec 2021 00:30:10 +0100
|
||||||
Message-ID: <CAAvm79YP0C=Rt1N=KWmSUBB87KK2rRChmdzKqF1vCwMEUiVzLQ@mail.gmail.com>
|
Message-ID: <CAAvm79YP0C=Rt1N=KWmSUBB87KK2rRChmdzKqF1vCwMEUiVzLQ@mail.gmail.com>
|
||||||
Subject: and one more
|
Subject: and one more
|
||||||
From: Phil <phil@example.com>
|
From: Phil <phil@example.com>
|
||||||
|
@ -148,60 +177,61 @@ so i'm gonna fill the rest of this with AAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
and with BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
|
and with BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
|
||||||
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
|
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
|
||||||
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
|
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
|
||||||
that should do it
|
that should do it
|
||||||
|
.
|
||||||
`
|
`
|
||||||
conf, backend := newTestBackend(t, func(w http.ResponseWriter, r *http.Request) {
|
s, c, conf, scanner := newTestSMTPServer(t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
expected := `you know this is a string.
|
expected := `you know this is a string.
|
||||||
it's a long string.
|
it's a long string.
|
||||||
it's supposed to be longer than the max message length
|
it's supposed to be longer than the max message length
|
||||||
|
@ -214,68 +244,71 @@ so i'm gonna fill the rest of this with AAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
......................................................................
|
pppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
|
||||||
and with BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
|
and with BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
|
||||||
BBBBBBBBBBBBBBBBBBBBBBBBB`
|
BBBBBBBBBBBBBBBBBBBBBBBBB`
|
||||||
require.Equal(t, 4096, len(expected)) // Sanity check
|
require.Equal(t, 4096, len(expected)) // Sanity check
|
||||||
require.Equal(t, expected, readAll(t, r.Body))
|
require.Equal(t, expected, readAll(t, r.Body))
|
||||||
})
|
})
|
||||||
|
defer s.Close()
|
||||||
|
defer c.Close()
|
||||||
conf.SMTPServerAddrPrefix = ""
|
conf.SMTPServerAddrPrefix = ""
|
||||||
session, _ := backend.AnonymousLogin(fakeConnState(t, "1.2.3.4"))
|
writeAndReadUntilLine(t, email, c, scanner, "250 2.0.0 OK: queued")
|
||||||
require.Nil(t, session.Mail("phil@example.com", smtp.MailOptions{}))
|
|
||||||
require.Nil(t, session.Rcpt("mytopic@ntfy.sh"))
|
|
||||||
require.Nil(t, session.Data(strings.NewReader(email)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSmtpBackend_Unsupported(t *testing.T) {
|
func TestSmtpBackend_Unsupported(t *testing.T) {
|
||||||
email := `Date: Tue, 28 Dec 2021 00:30:10 +0100
|
email := `EHLO example.com
|
||||||
|
MAIL FROM: phil@example.com
|
||||||
|
RCPT TO: ntfy-mytopic@ntfy.sh
|
||||||
|
DATA
|
||||||
|
Date: Tue, 28 Dec 2021 00:30:10 +0100
|
||||||
Message-ID: <CAAvm79YP0C=Rt1N=KWmSUBB87KK2rRChmdzKqF1vCwMEUiVzLQ@mail.gmail.com>
|
Message-ID: <CAAvm79YP0C=Rt1N=KWmSUBB87KK2rRChmdzKqF1vCwMEUiVzLQ@mail.gmail.com>
|
||||||
Subject: and one more
|
Subject: and one more
|
||||||
From: Phil <phil@example.com>
|
From: Phil <phil@example.com>
|
||||||
|
@ -283,34 +316,89 @@ To: mytopic@ntfy.sh
|
||||||
Content-Type: text/SOMETHINGELSE
|
Content-Type: text/SOMETHINGELSE
|
||||||
|
|
||||||
what's up
|
what's up
|
||||||
|
.
|
||||||
`
|
`
|
||||||
conf, backend := newTestBackend(t, func(http.ResponseWriter, *http.Request) {
|
s, c, _, scanner := newTestSMTPServer(t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
// Nothing.
|
t.Fatal("This should not be called")
|
||||||
})
|
})
|
||||||
conf.SMTPServerAddrPrefix = ""
|
defer s.Close()
|
||||||
session, _ := backend.Login(fakeConnState(t, "1.2.3.4"), "user", "pass")
|
defer c.Close()
|
||||||
require.Nil(t, session.Mail("phil@example.com", smtp.MailOptions{}))
|
writeAndReadUntilLine(t, email, c, scanner, "554 5.0.0 Error: transaction failed, blame it on the weather: unsupported content type")
|
||||||
require.Nil(t, session.Rcpt("mytopic@ntfy.sh"))
|
|
||||||
require.Equal(t, errUnsupportedContentType, session.Data(strings.NewReader(email)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTestBackend(t *testing.T, handler func(http.ResponseWriter, *http.Request)) (*Config, *smtpBackend) {
|
func TestSmtpBackend_InvalidAddress(t *testing.T) {
|
||||||
conf := newTestConfig(t)
|
email := `EHLO example.com
|
||||||
|
MAIL FROM: phil@example.com
|
||||||
|
RCPT TO: unsupported@ntfy.sh
|
||||||
|
DATA
|
||||||
|
Date: Tue, 28 Dec 2021 00:30:10 +0100
|
||||||
|
Subject: and one more
|
||||||
|
From: Phil <phil@example.com>
|
||||||
|
To: mytopic@ntfy.sh
|
||||||
|
Content-Type: text/plain
|
||||||
|
|
||||||
|
what's up
|
||||||
|
.
|
||||||
|
`
|
||||||
|
s, c, _, scanner := newTestSMTPServer(t, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
t.Fatal("This should not be called")
|
||||||
|
})
|
||||||
|
defer s.Close()
|
||||||
|
defer c.Close()
|
||||||
|
writeAndReadUntilLine(t, email, c, scanner, "451 4.0.0 invalid address")
|
||||||
|
}
|
||||||
|
|
||||||
|
type smtpHandlerFunc func(http.ResponseWriter, *http.Request)
|
||||||
|
|
||||||
|
func newTestSMTPServer(t *testing.T, handler smtpHandlerFunc) (s *smtp.Server, c net.Conn, conf *Config, scanner *bufio.Scanner) {
|
||||||
|
conf = newTestConfig(t)
|
||||||
conf.SMTPServerListen = ":25"
|
conf.SMTPServerListen = ":25"
|
||||||
conf.SMTPServerDomain = "ntfy.sh"
|
conf.SMTPServerDomain = "ntfy.sh"
|
||||||
conf.SMTPServerAddrPrefix = "ntfy-"
|
conf.SMTPServerAddrPrefix = "ntfy-"
|
||||||
backend := newMailBackend(conf, handler)
|
backend := newMailBackend(conf, handler)
|
||||||
return conf, backend
|
l, err := net.Listen("tcp", "127.0.0.1:0")
|
||||||
}
|
|
||||||
|
|
||||||
func fakeConnState(t *testing.T, remoteAddr string) *smtp.ConnectionState {
|
|
||||||
ip, err := net.ResolveIPAddr("ip", remoteAddr)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
return &smtp.ConnectionState{
|
s = smtp.NewServer(backend)
|
||||||
Hostname: "myhostname",
|
s.Domain = conf.SMTPServerDomain
|
||||||
LocalAddr: ip,
|
s.AllowInsecureAuth = true
|
||||||
RemoteAddr: ip,
|
go func() {
|
||||||
|
require.Nil(t, s.Serve(l))
|
||||||
|
}()
|
||||||
|
c, err = net.Dial("tcp", l.Addr().String())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
scanner = bufio.NewScanner(c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeAndReadUntilLine(t *testing.T, email string, conn net.Conn, scanner *bufio.Scanner, expectedLine string) {
|
||||||
|
_, err := io.WriteString(conn, email)
|
||||||
|
require.Nil(t, err)
|
||||||
|
readUntilLine(t, conn, scanner, expectedLine)
|
||||||
|
}
|
||||||
|
|
||||||
|
func readUntilLine(t *testing.T, conn net.Conn, scanner *bufio.Scanner, expectedLine string) {
|
||||||
|
cancelChan := make(chan bool)
|
||||||
|
go func() {
|
||||||
|
select {
|
||||||
|
case <-cancelChan:
|
||||||
|
case <-time.After(3 * time.Second):
|
||||||
|
conn.Close()
|
||||||
|
t.Error("Failed waiting for expected output")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
var output string
|
||||||
|
for scanner.Scan() {
|
||||||
|
text := scanner.Text()
|
||||||
|
if strings.TrimSpace(text) == expectedLine {
|
||||||
|
cancelChan <- true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
output += text + "\n"
|
||||||
|
//fmt.Println(text)
|
||||||
|
}
|
||||||
|
t.Fatalf("Expected line '%s' not found in output:\n%s", expectedLine, output)
|
||||||
}
|
}
|
||||||
|
|
13185
web/package-lock.json
generated
13185
web/package-lock.json
generated
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue