1
0
mirror of https://github.com/jhillyerd/inbucket.git synced 2025-12-19 10:37:01 +00:00

Merge branch 'release/3.0.0-rc2'

This commit is contained in:
James Hillyerd
2021-07-31 16:40:17 -07:00
18 changed files with 1891 additions and 1773 deletions

36
.github/workflows/build-and-test.yml vendored Normal file
View File

@@ -0,0 +1,36 @@
name: Build and Test
on:
pull_request:
jobs:
go-build:
runs-on: ubuntu-latest
strategy:
matrix:
go: [ '1.16', '1.15' ]
name: Go ${{ matrix.go }} build
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Setup Go
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go }}
- name: Build and test
run: |
go build ./...
go test -race -coverprofile=profile.cov ./...
- name: Send coverage
uses: shogo82148/actions-goveralls@v1
with:
path-to-profile: profile.cov
flag-name: Go-${{ matrix.go }}
parallel: true
coverage:
needs: go-build
name: Test Coverage
runs-on: ubuntu-latest
steps:
- uses: shogo82148/actions-goveralls@v1
with:
parallel-finished: true

View File

@@ -14,11 +14,11 @@ jobs:
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: 1.15 go-version: 1.16
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v1 uses: actions/setup-node@v1
with: with:
node-version: '10.x' node-version: '14.x'
- name: Setup Elm - name: Setup Elm
uses: jorelali/setup-elm@v2 uses: jorelali/setup-elm@v2
with: with:

View File

@@ -74,11 +74,15 @@ nfpms:
maintainer: github@hillyerd.com maintainer: github@hillyerd.com
description: All-in-one disposable webmail service. description: All-in-one disposable webmail service.
license: MIT license: MIT
files: contents:
"ui/dist/**/*": "/usr/local/share/inbucket/ui" - src: "ui/dist/**/*"
config_files: dst: "/usr/local/share/inbucket/ui"
"etc/linux/inbucket.service": "/lib/systemd/system/inbucket.service" - src: "etc/linux/inbucket.service"
"ui/greeting.html": "/etc/inbucket/greeting.html" dst: "/lib/systemd/system/inbucket.service"
type: config|noreplace
- src: "ui/greeting.html"
dst: "/etc/inbucket/greeting.html"
type: config|noreplace
snapshot: snapshot:
name_template: SNAPSHOT-{{ .Commit }} name_template: SNAPSHOT-{{ .Commit }}

View File

@@ -1,30 +0,0 @@
dist: bionic
env:
global:
- GO111MODULE=on
language: go
install:
- "go get golang.org/x/lint/golint"
- "make deps"
jobs:
include:
- go: "1.14.x"
- go: "1.15.x"
- language: elm
elm: "latest-0.19.1"
elm_format: "latest-0.19.1"
elm_test: "latest-0.19.1"
node_js: "10.16.0"
install:
- "cd ui"
- "npm ci"
script:
- "elm-format --validate ."
- "npm run build"
stages:
- test

View File

@@ -4,7 +4,22 @@ Change Log
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/). This project adheres to [Semantic Versioning](http://semver.org/).
## [v3.0.0-rc1] ## [Unreleased]
## [v3.0.0-rc2] - 2021-07-31
### Added
- Support for SMTP AUTH (#197, thanks makarchuk)
- Dark mode support (#218, thanks nerones)
### Fixed
- Prevent potential click jacking (#190, thanks stuartskelton)
- Error on 8 character long SMTP commands (#221)
- Allow empty username and password during AUTH (#225)
## [v3.0.0-rc1] - 2020-09-24
### Added ### Added
- Refresh button to reload mailbox contents - Refresh button to reload mailbox contents
@@ -15,7 +30,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
fonts fonts
## [v3.0.0-beta3] ## [v3.0.0-beta3] - 2020-09-04
### Added ### Added
- Docker `HEALTHCHECK` - Docker `HEALTHCHECK`
@@ -31,7 +46,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- Allow empty SMTP `MAIL FROM:<>` - Allow empty SMTP `MAIL FROM:<>`
## [v3.0.0-beta2] ## [v3.0.0-beta2] - 2019-08-17
### Added ### Added
- Ability to name mailboxes after domain of email recipient, set via - Ability to name mailboxes after domain of email recipient, set via
@@ -47,7 +62,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- Support for late EHLO, #141 - Support for late EHLO, #141
## [v3.0.0-beta1] ## [v3.0.0-beta1] - 2019-03-14
### Added ### Added
- `posix-millis` field to REST message and header responses for easier date - `posix-millis` field to REST message and header responses for easier date
@@ -60,12 +75,12 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- Update to enmime v0.5.0 - Update to enmime v0.5.0
## v2.1.0 ## v2.1.0 - 2018-12-15
No change from beta1. No change from beta1.
## [v2.1.0-beta1] ## [v2.1.0-beta1] - 2018-10-31
### Added ### Added
- Use Go 1.11 modules for reproducible builds. - Use Go 1.11 modules for reproducible builds.
@@ -239,6 +254,7 @@ No change from beta1.
specific message. specific message.
[Unreleased]: https://github.com/inbucket/inbucket/compare/master...develop [Unreleased]: https://github.com/inbucket/inbucket/compare/master...develop
[v3.0.0-rc2]: https://github.com/inbucket/inbucket/compare/v3.0.0-rc1...v3.0.0-rc2
[v3.0.0-rc1]: https://github.com/inbucket/inbucket/compare/v3.0.0-beta3...v3.0.0-rc1 [v3.0.0-rc1]: https://github.com/inbucket/inbucket/compare/v3.0.0-beta3...v3.0.0-rc1
[v3.0.0-beta3]: https://github.com/inbucket/inbucket/compare/v3.0.0-beta2...v3.0.0-beta3 [v3.0.0-beta3]: https://github.com/inbucket/inbucket/compare/v3.0.0-beta2...v3.0.0-beta3
[v3.0.0-beta2]: https://github.com/inbucket/inbucket/compare/v3.0.0-beta1...v3.0.0-beta2 [v3.0.0-beta2]: https://github.com/inbucket/inbucket/compare/v3.0.0-beta1...v3.0.0-beta2

View File

@@ -1,7 +1,7 @@
# Docker build file for Inbucket: https://www.inbucket.org/ # Docker build file for Inbucket: https://www.inbucket.org/
# Install build-time dependencies # Install build-time dependencies
FROM golang:1.15-alpine3.12 as builder FROM golang:1.16-alpine3.13 as builder
RUN apk add --no-cache --virtual .build-deps g++ git make npm python3 RUN apk add --no-cache --virtual .build-deps g++ git make npm python3
WORKDIR /build WORKDIR /build
COPY . . COPY . .
@@ -24,7 +24,7 @@ WORKDIR /build/ui
RUN npm run build RUN npm run build
# Run in minimal image # Run in minimal image
FROM alpine:3.12 FROM alpine:3.13
RUN apk --no-cache add tzdata RUN apk --no-cache add tzdata
WORKDIR /opt/inbucket WORKDIR /opt/inbucket
RUN mkdir bin defaults ui RUN mkdir bin defaults ui

19
go.mod
View File

@@ -2,24 +2,19 @@ module github.com/inbucket/inbucket
require ( require (
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/gogs/chardet v0.0.0-20191104214054-4b6791f73a28 // indirect
github.com/google/subcommands v1.2.0 github.com/google/subcommands v1.2.0
github.com/gorilla/css v1.0.0 github.com/gorilla/css v1.0.0
github.com/gorilla/mux v1.8.0 github.com/gorilla/mux v1.8.0
github.com/gorilla/websocket v1.4.2 github.com/gorilla/websocket v1.4.2
github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7 // indirect github.com/jhillyerd/enmime v0.9.1
github.com/jhillyerd/enmime v0.8.1
github.com/jhillyerd/goldiff v0.1.0 github.com/jhillyerd/goldiff v0.1.0
github.com/kelseyhightower/envconfig v1.4.0 github.com/kelseyhightower/envconfig v1.4.0
github.com/mattn/go-runewidth v0.0.9 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/microcosm-cc/bluemonday v1.0.4 github.com/microcosm-cc/bluemonday v1.0.15
github.com/olekukonko/tablewriter v0.0.4 // indirect github.com/rs/zerolog v1.23.0
github.com/pkg/errors v0.9.1 // indirect github.com/stretchr/testify v1.7.0
github.com/rs/zerolog v1.20.0 golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985
github.com/stretchr/testify v1.6.1 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
golang.org/x/net v0.0.0-20200923182212-328152dc79b1
golang.org/x/text v0.3.3 // indirect
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect
) )
go 1.13 go 1.13

86
go.sum
View File

@@ -1,17 +1,15 @@
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a h1:MISbI8sU/PSK/ztvmWKFcI7UGb5/HQT7B+i3a2myKgI= github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a h1:MISbI8sU/PSK/ztvmWKFcI7UGb5/HQT7B+i3a2myKgI=
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a/go.mod h1:2GxOXOlEPAMFPfp014mK1SWq8G8BN8o7/dfYqJrVGn8= github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a/go.mod h1:2GxOXOlEPAMFPfp014mK1SWq8G8BN8o7/dfYqJrVGn8=
github.com/chris-ramon/douceur v0.2.0 h1:IDMEdxlEUUBYBKE4z/mJnFyVXox+MjuEVDJNN27glkU= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/chris-ramon/douceur v0.2.0/go.mod h1:wDW5xjJdeoMm1mRt4sD4c/LbF/mWdEpRXQKjTR8nIBE=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw= github.com/go-test/deep v1.0.7 h1:/VSMRlnY/JSyqxQUzQLKVMAskpY/NZKFA5j2P+0pP2M=
github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-test/deep v1.0.7/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8=
github.com/gogs/chardet v0.0.0-20150115103509-2404f7772561 h1:aBzukfDxQlCTVS0NBUjI5YA3iVeaZ9Tb5PxNrrIP1xs= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogs/chardet v0.0.0-20150115103509-2404f7772561/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14=
github.com/gogs/chardet v0.0.0-20191104214054-4b6791f73a28 h1:gBeyun7mySAKWg7Fb0GOcv0upX9bdaZScs8QcRo8mEY= github.com/gogs/chardet v0.0.0-20191104214054-4b6791f73a28 h1:gBeyun7mySAKWg7Fb0GOcv0upX9bdaZScs8QcRo8mEY=
github.com/gogs/chardet v0.0.0-20191104214054-4b6791f73a28/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14= github.com/gogs/chardet v0.0.0-20191104214054-4b6791f73a28/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14=
github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN9oE= github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN9oE=
@@ -22,68 +20,70 @@ github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/jaytaylor/html2text v0.0.0-20190408195923-01ec452cbe43 h1:jTkyeF7NZ5oIr0ESmcrpiDgAfoidCBF4F5kJhjtaRwE=
github.com/jaytaylor/html2text v0.0.0-20190408195923-01ec452cbe43/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7 h1:g0fAGBisHaEQ0TRq1iBvemFRf+8AEWEmBESSiWB3Vsc= github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7 h1:g0fAGBisHaEQ0TRq1iBvemFRf+8AEWEmBESSiWB3Vsc=
github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk= github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
github.com/jhillyerd/enmime v0.8.1 h1:Kz4xj3sJJ4Ju8e+w/7v9H4Matv5ijPgv7UkhPf+C15I= github.com/jhillyerd/enmime v0.9.1 h1:HcC2WZA6dMCobs8WeyF/6FRSvdRCrr8O+UiLBae4eNE=
github.com/jhillyerd/enmime v0.8.1/go.mod h1:MBHs3ugk03NGjMM6PuRynlKf+HA5eSillZ+TRCm73AE= github.com/jhillyerd/enmime v0.9.1/go.mod h1:S5ge4lnv/dDDBbAWwtoOFlj14NHiXdw/EqMB2lJz3b8=
github.com/jhillyerd/goldiff v0.1.0 h1:7JzKPKVwAg1GzrbnsToYzq3Y5+S7dXM4hgEYiOzaf4A= github.com/jhillyerd/goldiff v0.1.0 h1:7JzKPKVwAg1GzrbnsToYzq3Y5+S7dXM4hgEYiOzaf4A=
github.com/jhillyerd/goldiff v0.1.0/go.mod h1:WeDal6DTqhbMhNkf5REzWCIvKl3JWs0Q9omZ/huIWAs= github.com/jhillyerd/goldiff v0.1.0/go.mod h1:WeDal6DTqhbMhNkf5REzWCIvKl3JWs0Q9omZ/huIWAs=
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/microcosm-cc/bluemonday v1.0.4 h1:p0L+CTpo/PLFdkoPcJemLXG+fpMD7pYOoDEq1axMbGg= github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/microcosm-cc/bluemonday v1.0.4/go.mod h1:8iwZnFn2CDDNZ0r6UXhF4xawGvzaqzCRa1n3/lO3W2w= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/olekukonko/tablewriter v0.0.1 h1:b3iUnf1v+ppJiOfNX4yxxqfWKMQPZR5yoh8urCTFX88= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/microcosm-cc/bluemonday v1.0.15 h1:J4uN+qPng9rvkBZBoBb8YGR+ijuklIMpSOZZLjYpbeY=
github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8= github.com/microcosm-cc/bluemonday v1.0.15/go.mod h1:ZLvAzeakRwrGnzQEvstVzVt3ZpqOF2+sdFr0Om+ce30=
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.20.0 h1:38k9hgtUBdxFwE34yS8rTHmHBa4eN16E4DJlv177LNs= github.com/rs/zerolog v1.23.0 h1:UskrK+saS9P9Y789yNNulYKdARjPZuS35B8gJF2x60g=
github.com/rs/zerolog v1.20.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo= github.com/rs/zerolog v1.23.0/go.mod h1:6c7hFfxPOy7TacJc4Fcdi24/J0NKYGzjG8FWRI916Qo=
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca h1:NugYot0LIVPxTvN8n+Kvkn6TrbMyxQiuvKdEwFdR9vI=
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo= github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo=
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM= github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
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/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210501142056-aec3718b3fa0/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20200923182212-328152dc79b1 h1:Iu68XRPd67wN4aRGGWwwq6bZo/25jR6uu52l/j2KkUE= golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20200923182212-328152dc79b1/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985 h1:4CSI6oo7cOjJKajidEljs9h+uP0rRZBPPPhcCbj5mw8=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
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=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
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 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
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.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
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=
golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -25,10 +25,27 @@ const (
// timeStampFormat to use in Received header. // timeStampFormat to use in Received header.
timeStampFormat = "Mon, 02 Jan 2006 15:04:05 -0700 (MST)" timeStampFormat = "Mon, 02 Jan 2006 15:04:05 -0700 (MST)"
// Messages sent to user during LOGIN auth procedure. Can vary, but values are taken directly
// from spec https://tools.ietf.org/html/draft-murchison-sasl-login-00
// usernameChallenge sent when inviting user to provide username. Is base64 encoded string
// `User Name`
usernameChallenge = "VXNlciBOYW1lAA=="
// passwordChallenge sent when inviting user to provide password. Is base64 encoded string
// `Password`
passwordChallenge = "UGFzc3dvcmQA"
)
const (
// GREET State: Waiting for HELO // GREET State: Waiting for HELO
GREET State = iota GREET State = iota
// READY State: Got HELO, waiting for MAIL // READY State: Got HELO, waiting for MAIL
READY READY
// LOGIN State: Got AUTH LOGIN command, expecting Username
LOGIN
// PASSWORD State: Got Username, expecting password
PASSWORD
// MAIL State: Got MAIL, accepting RCPTs // MAIL State: Got MAIL, accepting RCPTs
MAIL MAIL
// DATA State: Got DATA, waiting for "." // DATA State: Got DATA, waiting for "."
@@ -76,6 +93,7 @@ var commands = map[string]bool{
"QUIT": true, "QUIT": true,
"TURN": true, "TURN": true,
"STARTTLS": true, "STARTTLS": true,
"AUTH": true,
} }
// Session holds the state of an SMTP session // Session holds the state of an SMTP session
@@ -153,6 +171,16 @@ func (s *Server) startSession(id int, conn net.Conn) {
} }
line, err := ssn.readLine() line, err := ssn.readLine()
if err == nil { if err == nil {
//Handle LOGIN/PASSWORD states here, because they don't expect a command
switch ssn.state {
case LOGIN:
ssn.loginHandler(line)
continue
case PASSWORD:
ssn.passwordHandler(line)
continue
}
if cmd, arg, ok := ssn.parseCmd(line); ok { if cmd, arg, ok := ssn.parseCmd(line); ok {
// Check against valid SMTP commands // Check against valid SMTP commands
if cmd == "" { if cmd == "" {
@@ -219,7 +247,7 @@ func (s *Server) startSession(id int, conn net.Conn) {
} }
break break
} }
// not an EOF // Not an EOF
ssn.logger.Warn().Msgf("Connection error: %v", err) ssn.logger.Warn().Msgf("Connection error: %v", err)
if netErr, ok := err.(net.Error); ok { if netErr, ok := err.(net.Error); ok {
if netErr.Timeout() { if netErr.Timeout() {
@@ -257,9 +285,10 @@ func (s *Session) greetHandler(cmd string, arg string) {
return return
} }
s.remoteDomain = domain s.remoteDomain = domain
// features before SIZE per RFC // Features before SIZE per RFC
s.send("250-" + readyBanner) s.send("250-" + readyBanner)
s.send("250-8BITMIME") s.send("250-8BITMIME")
s.send("250-AUTH PLAIN LOGIN")
if s.Server.config.TLSEnabled && s.Server.tlsConfig != nil && s.tlsState == nil { if s.Server.config.TLSEnabled && s.Server.tlsConfig != nil && s.tlsState == nil {
s.send("250-STARTTLS") s.send("250-STARTTLS")
} }
@@ -281,30 +310,71 @@ func parseHelloArgument(arg string) (string, error) {
return domain, nil return domain, nil
} }
func (s *Session) loginHandler(line string) {
// Content and length of username is ignored.
s.send(fmt.Sprintf("334 %v", passwordChallenge))
s.enterState(PASSWORD)
}
func (s *Session) passwordHandler(line string) {
// Content and length of password is ignored.
s.send("235 Authentication successful")
s.enterState(READY)
}
// READY state -> waiting for MAIL // READY state -> waiting for MAIL
// AUTH can change
func (s *Session) readyHandler(cmd string, arg string) { func (s *Session) readyHandler(cmd string, arg string) {
if cmd == "STARTTLS" { if cmd == "STARTTLS" {
if !s.Server.config.TLSEnabled { if !s.Server.config.TLSEnabled {
// invalid command since unconfigured // Invalid command since TLS unconfigured.
s.logger.Debug().Msgf("454 TLS unavailable on the server") s.logger.Debug().Msgf("454 TLS unavailable on the server")
s.send("454 TLS unavailable on the server") s.send("454 TLS unavailable on the server")
return return
} }
if s.tlsState != nil { if s.tlsState != nil {
// tls state previously valid // TLS state previously valid.
s.logger.Debug().Msg("454 A TLS session already agreed upon.") s.logger.Debug().Msg("454 A TLS session already agreed upon.")
s.send("454 A TLS session already agreed upon.") s.send("454 A TLS session already agreed upon.")
return return
} }
s.logger.Debug().Msg("Initiating TLS context.") s.logger.Debug().Msg("Initiating TLS context.")
// Start TLS connection handshake.
s.send("220 STARTTLS") s.send("220 STARTTLS")
// start tls connection handshake
tlsConn := tls.Server(s.conn, s.Server.tlsConfig) tlsConn := tls.Server(s.conn, s.Server.tlsConfig)
s.conn = tlsConn s.conn = tlsConn
s.text = textproto.NewConn(s.conn) s.text = textproto.NewConn(s.conn)
s.tlsState = new(tls.ConnectionState) s.tlsState = new(tls.ConnectionState)
*s.tlsState = tlsConn.ConnectionState() *s.tlsState = tlsConn.ConnectionState()
s.enterState(GREET) s.enterState(GREET)
} else if cmd == "AUTH" {
args := strings.SplitN(arg, " ", 3)
authMethod := args[0]
switch authMethod {
case "PLAIN":
{
if len(args) != 2 {
s.send("500 Bad auth arguments")
s.logger.Warn().Msgf("Bad auth attempt: %q", arg)
return
}
s.logger.Info().Msgf("Accepting credentials: %q", args[1])
s.send("235 2.7.0 Authentication successful")
return
}
case "LOGIN":
{
s.send(fmt.Sprintf("334 %v", usernameChallenge))
s.enterState(LOGIN)
return
}
default:
{
s.send(fmt.Sprintf("500 Unsupported AUTH method: %v", authMethod))
return
}
}
} else if cmd == "MAIL" { } else if cmd == "MAIL" {
// Capture group 1: from address. 2: optional params. // Capture group 1: from address. 2: optional params.
m := fromRegex.FindStringSubmatch(arg) m := fromRegex.FindStringSubmatch(arg)
@@ -518,30 +588,28 @@ func (s *Session) readLine() (line string, err error) {
func (s *Session) parseCmd(line string) (cmd string, arg string, ok bool) { func (s *Session) parseCmd(line string) (cmd string, arg string, ok bool) {
line = strings.TrimRight(line, "\r\n") line = strings.TrimRight(line, "\r\n")
l := len(line)
// Find length of command or entire line.
hasArg := true
l := strings.IndexByte(line, ' ')
if l == -1 {
hasArg = false
l = len(line)
}
switch { switch {
case l == 0: case l == 0:
return "", "", true return "", "", true
case l < 4: case l < 4:
s.logger.Warn().Msgf("Command too short: %q", line) s.logger.Warn().Msgf("Command too short: %q", line)
return "", "", false return "", "", false
case l == 4 || l == 8: }
if hasArg {
return strings.ToUpper(line[0:l]), strings.Trim(line[l+1:], " "), true
}
return strings.ToUpper(line), "", true return strings.ToUpper(line), "", true
case l == 5:
// Too long to be only command, too short to have args
s.logger.Warn().Msgf("Mangled command: %q", line)
return "", "", false
}
// If we made it here, command is long enough to have args
if line[4] != ' ' {
// There wasn't a space after the command?
s.logger.Warn().Msgf("Mangled command: %q", line)
return "", "", false
}
// I'm not sure if we should trim the args or not, but we will for now
return strings.ToUpper(line[0:4]), strings.Trim(line[5:], " "), true
} }
// parseArgs takes the arguments proceeding a command and files them // parseArgs takes the arguments proceeding a command and files them

View File

@@ -56,6 +56,9 @@ func TestGreetState(t *testing.T) {
if err := playSession(t, server, []scriptStep{{"helo 127.0.0.1", 250}}); err != nil { if err := playSession(t, server, []scriptStep{{"helo 127.0.0.1", 250}}); err != nil {
t.Error(err) t.Error(err)
} }
if err := playSession(t, server, []scriptStep{{"HELO ABC", 250}}); err != nil {
t.Error(err)
}
// Valid EHLOs // Valid EHLOs
if err := playSession(t, server, []scriptStep{{"EHLO mydomain", 250}}); err != nil { if err := playSession(t, server, []scriptStep{{"EHLO mydomain", 250}}); err != nil {
@@ -70,6 +73,9 @@ func TestGreetState(t *testing.T) {
if err := playSession(t, server, []scriptStep{{"ehlo 127.0.0.1", 250}}); err != nil { if err := playSession(t, server, []scriptStep{{"ehlo 127.0.0.1", 250}}); err != nil {
t.Error(err) t.Error(err)
} }
if err := playSession(t, server, []scriptStep{{"EHLO a", 250}}); err != nil {
t.Error(err)
}
if t.Failed() { if t.Failed() {
// Wait for handler to finish logging // Wait for handler to finish logging
@@ -108,6 +114,50 @@ func TestEmptyEnvelope(t *testing.T) {
} }
} }
// Test AUTH
func TestAuth(t *testing.T) {
ds := test.NewStore()
server, logbuf, teardown := setupSMTPServer(ds)
defer teardown()
// PLAIN AUTH
script := []scriptStep{
{"EHLO localhost", 250},
{"AUTH PLAIN aW5idWNrZXQ6cGFzc3dvcmQK", 235},
{"RSET", 250},
{"AUTH GSSAPI aW5idWNrZXQ6cGFzc3dvcmQK", 500},
{"RSET", 250},
{"AUTH PLAIN", 500},
{"RSET", 250},
{"AUTH PLAIN aW5idWNrZXQ6cG Fzc3dvcmQK", 500},
}
if err := playSession(t, server, script); err != nil {
t.Error(err)
}
// LOGIN AUTH
script = []scriptStep{
{"EHLO localhost", 250},
{"AUTH LOGIN", 334}, // Test with user/pass present.
{"username", 334},
{"password", 235},
{"RSET", 250},
{"AUTH LOGIN", 334}, // Test with empty user/pass.
{"", 334},
{"", 235},
}
if err := playSession(t, server, script); err != nil {
t.Error(err)
}
if t.Failed() {
// Wait for handler to finish logging
time.Sleep(2 * time.Second)
// Dump buffered log data if there was a failure
_, _ = io.Copy(os.Stderr, logbuf)
}
}
// Test commands in READY state // Test commands in READY state
func TestReadyState(t *testing.T) { func TestReadyState(t *testing.T) {
ds := test.NewStore() ds := test.NewStore()
@@ -159,6 +209,15 @@ func TestReadyState(t *testing.T) {
t.Error(err) t.Error(err)
} }
// Test Start TLS parsing.
script = []scriptStep{
{"HELO localhost", 250},
{"STARTTLS", 454}, // TLS unconfigured.
}
if err := playSession(t, server, script); err != nil {
t.Error(err)
}
if t.Failed() { if t.Failed() {
// Wait for handler to finish logging // Wait for handler to finish logging
time.Sleep(2 * time.Second) time.Sleep(2 * time.Second)

View File

@@ -94,6 +94,8 @@ func spaTemplateHandler(tmpl *template.Template, basePath string,
BasePath: basePath, BasePath: basePath,
} }
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
// ensure we do now allow click jacking
w.Header().Set("X-Frame-Options", "SameOrigin")
err := tmpl.Execute(w, tmplData) err := tmpl.Execute(w, tmplData)
if err != nil { if err != nil {
log.Error().Str("module", "web").Str("remote", req.RemoteAddr).Str("proto", req.Proto). log.Error().Str("module", "web").Str("remote", req.RemoteAddr).Str("proto", req.Proto).

View File

@@ -65,7 +65,7 @@ func TestMaxSize(t *testing.T) {
go func(mailbox string) { go func(mailbox string) {
err := s.PurgeMessages(mailbox) err := s.PurgeMessages(mailbox)
if err != nil { if err != nil {
t.Fatal(err) panic(err) // Cannot call t.Fatal from non-test goroutine.
} }
wg.Done() wg.Done()
}(mailbox) }(mailbox)

View File

@@ -7,11 +7,12 @@ stdenv.mkDerivation rec {
elmPackages.elm elmPackages.elm
elmPackages.elm-analyse elmPackages.elm-analyse
elmPackages.elm-format elmPackages.elm-format
elmPackages.elm-json
elmPackages.elm-language-server elmPackages.elm-language-server
elmPackages.elm-test elmPackages.elm-test
go go
golint golint
nodejs-10_x nodejs-14_x
rpm rpm
swaks swaks
]; ];

3069
ui/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -14,20 +14,20 @@
"opensans-npm-webfont": "^1.0.0" "opensans-npm-webfont": "^1.0.0"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.11.6", "@babel/core": "^7.14.8",
"@babel/preset-env": "^7.11.5", "@babel/preset-env": "^7.14.8",
"@fortawesome/fontawesome-free": "^5.14.0", "@fortawesome/fontawesome-free": "^5.15.3",
"@webcomponents/webcomponentsjs": "^2.4.4", "@webcomponents/webcomponentsjs": "^2.5.0",
"babel-loader": "^8.1.0", "babel-loader": "^8.2.2",
"css-loader": "^4.3.0", "css-loader": "^4.3.0",
"elm-hot-webpack-loader": "^1.1.7", "elm-hot-webpack-loader": "^1.1.8",
"elm-webpack-loader": "^7.0.1", "elm-webpack-loader": "^7.0.1",
"file-loader": "^6.1.0", "file-loader": "^6.2.0",
"html-webpack-plugin": "^4.5.0", "html-webpack-plugin": "^4.5.2",
"node-elm-compiler": "^5.0.5", "node-elm-compiler": "^5.0.6",
"style-loader": "^1.2.1", "style-loader": "^1.3.0",
"webpack": "^4.44.2", "webpack": "^4.46.0",
"webpack-cli": "^3.3.12", "webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.0" "webpack-dev-server": "^3.11.2"
} }
} }

View File

@@ -76,7 +76,7 @@
.message-list { .message-list {
display: block; display: block;
overflow-y: scroll; overflow-y: auto;
} }
.message-list-controls { .message-list-controls {

View File

@@ -11,6 +11,68 @@
--selected-color: #eee; --selected-color: #eee;
--focused-color: #fff; --focused-color: #fff;
--focused-bg-color: #337ab7; --focused-bg-color: #337ab7;
--input-bg: white;
--input-bg-active: white;
--btn-default-bg-color: #337ab7;
--btn-default-bg-image: linear-gradient(to bottom, #337ab7 0, #265a88 100%);
--btn-default-color: #ffffff;
--btn-danger-bg-color: #d9534f;
--btn-danger-bg-image: linear-gradient(to bottom, #d9534f 0, #c12e2a 100%);
--btn-light-bg-color: #eee;
--btn-light-bg-image: linear-gradient(to bottom, #f0f0f0 0, #e0e0e0 100%);
--monitor-header-bg: #e8e8e8;
--well-bg-color: #f5f5f5;
--well-bg-image: linear-gradient(to bottom, #e8e8e8 0, #f5f5f5 100%);
--well-warn-bg-color: #fff8cf;
--well-warn-bg-image: linear-gradient(to bottom, #fff899 0, #fff8cf 100%);
--well-warn-color: inherit;
--well-error-bg-color: #f58080;
--well-error-bg-image: linear-gradient(to bottom, #e86060 0, #f58080 100%);
--well-error-color: inherit;
--well-border: #e8e8e8;
}
@media (prefers-color-scheme: dark) {
:root {
--bg-color: #202124;
--primary-color: #bdc1c6;
--high-color: #8ab4f8;
--border-color: #5f6368;
--selected-color: #303134;
--input-bg: var(--bg-color);
--input-bg-active: rgb(48, 49, 52);
--btn-default-bg-color: #303134;
--btn-default-bg-image: none;
--btn-default-color: #e8eaed;
/*--btn-danger-bg-color: #d9534f;*/
--btn-danger-bg-image: none;
/*--btn-light-bg-color: #eee;*/
--btn-light-bg-image: none;
--monitor-header-bg: var(--selected-color);
--well-bg-color: var(--low-color);
--well-bg-image: none;
--well-warn-bg-color: #c3c099;
--well-warn-bg-image: none;
--well-warn-color: var(--bg-color);
--well-error-bg-color: #e86060;
--well-error-bg-image: none;
--well-error-color: var(--bg-color);
--well-border: var(--border-color);
}
} }
html, body, div, span, applet, object, iframe, html, body, div, span, applet, object, iframe,
@@ -39,7 +101,7 @@ time, mark, audio, video {
} }
a { a {
color: #337ab7; color: var(--high-color);
text-decoration: none; text-decoration: none;
} }
@@ -67,8 +129,8 @@ h1, h2, h3, h4, h5, h6, p {
/** SHARED */ /** SHARED */
a.button { a.button {
background-color: #337ab7; background-color: var(--btn-default-bg-color);
background-image: linear-gradient(to bottom, #337ab7 0, #265a88 100%); background-image: var(--btn-default-bg-image);
border: none; border: none;
border-radius: 4px; border-radius: 4px;
color: #fff; color: #fff;
@@ -82,11 +144,9 @@ a.button {
} }
.well { .well {
--light: #f5f5f5; background-color: var(--well-bg-color);
--dark: #e8e8e8; background-image: var(--well-bg-image);
background-color: var(--light); border: 1px solid var(--well-border);
background-image: linear-gradient(to bottom, var(--dark) 0, var(--light) 100%);
border: 1px solid var(--dark);
border-radius: 4px; border-radius: 4px;
box-shadow: 0 1px 2px rgba(0,0,0,.05); box-shadow: 0 1px 2px rgba(0,0,0,.05);
padding: 6px 10px; padding: 6px 10px;
@@ -98,8 +158,9 @@ a.button {
} }
.well-error { .well-error {
--light: #f58080; background-color: var(--well-error-bg-color);
--dark: #e86060; background-image: var(--well-error-bg-image);
color: var(--well-error-color);
} }
.well-error a { .well-error a {
@@ -108,8 +169,22 @@ a.button {
} }
.well-warn { .well-warn {
--light: #fff8cf; background-color: var(--well-warn-bg-color);
--dark: #fff899; background-image: var(--well-warn-bg-image);
color: var(--well-warn-color);
}
input {
border: 1px solid var(--border-color);
background-color: var(--input-bg);
}
@media (prefers-color-scheme: dark) {
input:focus-visible, input:hover {
outline: none;
border: 1px solid var(--input-bg-active);
background-color: var(--input-bg-active);
}
} }
/** APP */ /** APP */
@@ -237,11 +312,11 @@ h3 {
} }
.button-bar button { .button-bar button {
background-color: #337ab7; background-color: var(--btn-default-bg-color);
background-image: linear-gradient(to bottom, #337ab7 0, #265a88 100%); background-image: var(--btn-default-bg-image);
border: none; border: none;
border-radius: 4px; border-radius: 4px;
color: #fff; color: var(--btn-default-color);
display: inline-block; display: inline-block;
font-size: 12px; font-size: 12px;
font-style: normal; font-style: normal;
@@ -254,18 +329,22 @@ h3 {
text-shadow: 0 -1px 0 rgba(0,0,0,0.2); text-shadow: 0 -1px 0 rgba(0,0,0,0.2);
} }
.button-bar button:hover {
border: 1px solid var(--border-color);
}
.button-bar *:not(:last-child) { .button-bar *:not(:last-child) {
margin-right: 4px; margin-right: 4px;
} }
.button-bar button.danger { .button-bar button.danger {
background-color: #d9534f; background-color: var(--btn-danger-bg-color);
background-image: linear-gradient(to bottom, #d9534f 0, #c12e2a 100%); background-image: var(--btn-danger-bg-image);
} }
.button-bar button.light { .button-bar button.light {
background-color: #eee; background-color: var(--btn-light-bg-color);
background-image: linear-gradient(to bottom, #f0f0f0 0, #e0e0e0 100%); background-image: var(--btn-light-bg-image);
color: #000; color: #000;
} }
@@ -285,7 +364,7 @@ h3 {
} }
.metric-panel h2 { .metric-panel h2 {
background-image: linear-gradient(to bottom, #f5f5f5 0, #e8e8e8 100%); background-color: var(--monitor-header-bg);
font-size: 16px; font-size: 16px;
font-weight: 500; font-weight: 500;
padding: 10px; padding: 10px;

View File

@@ -2,16 +2,34 @@
:root { :root {
--navbar-color: #9d9d9d; --navbar-color: #9d9d9d;
--navbar-color-active: var(--navbar-color);
--navbar-bg: #222; --navbar-bg: #222;
--navbar-bg-active: #080808;
--navbar-bg-border-active: none;
--navbar-image: linear-gradient(to bottom, #3c3c3c 0, #222 100%); --navbar-image: linear-gradient(to bottom, #3c3c3c 0, #222 100%);
--navbar-height: 50px; --navbar-height: 50px;
--navbar-border-bottom: none;
} }
@media (prefers-color-scheme: dark) {
:root {
--navbar-color: #969ba1;
--navbar-color-active: #8ab4f8;
--navbar-bg: var(--bg-color);
--navbar-bg-active: none;
--navbar-bg-border-active: 3px solid var(--navbar-color-active);
--navbar-image: none;
--navbar-border-bottom: 1px solid var(--border-color);
}
}
.navbar { .navbar {
background-color: var(--navbar-bg); background-color: var(--navbar-bg);
background-image: var(--navbar-image); background-image: var(--navbar-image);
text-shadow: 0 -1px 0 rgba(0,0,0,0.2); text-shadow: 0 -1px 0 rgba(0,0,0,0.2);
min-height: var(--navbar-height); min-height: var(--navbar-height);
border-bottom: var(--navbar-border-bottom);
} }
.main-nav { .main-nav {
@@ -66,7 +84,9 @@
} }
li.navbar-active > *:first-child { li.navbar-active > *:first-child {
background-color: #080808; background-color: var(--navbar-bg-active);
color: var(--navbar-color-active);
border-bottom: var(--navbar-bg-border-active);
} }
li.navbar-active a, li.navbar-active a,
@@ -92,7 +112,6 @@ li.navbar-active span,
} }
.navbar-mailbox input { .navbar-mailbox input {
border: 1px solid var(--border-color);
border-radius: 4px; border-radius: 4px;
padding: 5px 10px; padding: 5px 10px;
width: 250px; width: 250px;