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-beta2'

This commit is contained in:
James Hillyerd
2019-08-17 16:11:12 -07:00
15 changed files with 1896 additions and 2328 deletions

View File

@@ -4,6 +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-beta2]
### Added
- Ability to name mailboxes after domain of email recipient, set via
`INBUCKET_MAILBOXNAMING`, thanks MatthewJohn.
### Changed
- Updated JavaScript dependencies.
- Updated Go dependencies.
- Updated Docker build: Go to 1.12, and Alpine Linux to 3.10
### Fixed
- URLs to view/download attachments from REST API, #138
- Support for late EHLO, #141
## [v3.0.0-beta1] ## [v3.0.0-beta1]
### Added ### Added
@@ -196,6 +212,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-beta2]: https://github.com/inbucket/inbucket/compare/v3.0.0-beta1...v3.0.0-beta2
[v3.0.0-beta1]: https://github.com/inbucket/inbucket/compare/v2.1.0...v3.0.0-beta1 [v3.0.0-beta1]: https://github.com/inbucket/inbucket/compare/v2.1.0...v3.0.0-beta1
[v2.1.0-beta1]: https://github.com/inbucket/inbucket/compare/v2.0.0...v2.1.0-beta1 [v2.1.0-beta1]: https://github.com/inbucket/inbucket/compare/v2.0.0...v2.1.0-beta1
[v2.0.0]: https://github.com/inbucket/inbucket/compare/v2.0.0-rc1...v2.0.0 [v2.0.0]: https://github.com/inbucket/inbucket/compare/v2.0.0-rc1...v2.0.0

View File

@@ -1,7 +1,7 @@
# Docker build file for Inbucket: https://www.inbucket.org/ # Docker build file for Inbucket: https://www.inbucket.org/
# Build # Build
FROM golang:1.11-alpine3.8 as builder FROM golang:1.12-alpine3.10 as builder
RUN apk add --no-cache --virtual .build-deps git make npm RUN apk add --no-cache --virtual .build-deps git make npm
WORKDIR /build WORKDIR /build
COPY . . COPY . .
@@ -16,7 +16,7 @@ RUN npm i
RUN npm run build RUN npm run build
# Run in minimal image # Run in minimal image
FROM alpine:3.8 FROM alpine:3.10
WORKDIR /opt/inbucket WORKDIR /opt/inbucket
RUN mkdir bin defaults ui RUN mkdir bin defaults ui
COPY --from=builder /build/inbucket bin COPY --from=builder /build/inbucket bin

View File

@@ -22,6 +22,13 @@ Please see the [Change Log] and [Issues List] for more details. If you'd like
to contribute code to the project check out [CONTRIBUTING.md]. to contribute code to the project check out [CONTRIBUTING.md].
## Docker
Inbucket has automated [Docker Image] builds via Docker Hub. The `stable` tag
tracks our `master` branch (releases), `latest` tracks our unstable
`development` branch.
## Homebrew Tap ## Homebrew Tap
(currently broken, being tracked in [issue (currently broken, being tracked in [issue
@@ -33,38 +40,52 @@ see the `README.md` there for installation instructions.
## Building from Source ## Building from Source
You will need a functioning [Go installation][Google Go] for this to work. You will need functioning [Go] and [Node.js] installations for this to work.
Grab the Inbucket source code and compile the daemon: ```sh
git clone https://github.com/inbucket/inbucket.git
cd inbucket/ui
npm i
npm run build
cd ..
go build ./cmd/inbucket
```
go get -v github.com/inbucket/inbucket/cmd/inbucket _Note:_ You may also use the included Makefile to build and test the Go binaries.
Edit etc/inbucket.conf and tailor to your environment. It should work on most Inbucket reads its configuration from environment variables, but comes with
Unix and OS X machines as is. Launch the daemon: built in sane defaults. It should work on most Unix and OS X machines as is.
Launch the daemon:
$GOPATH/bin/inbucket $GOPATH/src/github.com/inbucket/inbucket/etc/inbucket.conf ```sh
./inbucket
```
By default the SMTP server will be listening on localhost port 2500 and By default the SMTP server will be listening on localhost port 2500 and
the web interface will be available at [localhost:9000](http://localhost:9000/). the web interface will be available at [localhost:9000](http://localhost:9000/).
The Inbucket website has a more complete guide to See doc/[config.md] for more information on configuring Inbucket, but you will
[installing from source][From Source] likely find the [Configurator] tool easier to use.
## About ## About
Inbucket is written in [Google Go] Inbucket is written in [Go]
Inbucket is open source software released under the MIT License. The latest Inbucket is open source software released under the MIT License. The latest
version can be found at https://github.com/inbucket/inbucket version can be found at https://github.com/inbucket/inbucket
[Go API docs]: https://godoc.org/github.com/inbucket/inbucket/pkg/rest/client
[Build Status]: https://travis-ci.org/inbucket/inbucket [Build Status]: https://travis-ci.org/inbucket/inbucket
[Change Log]: https://github.com/inbucket/inbucket/blob/master/CHANGELOG.md [Change Log]: https://github.com/inbucket/inbucket/blob/master/CHANGELOG.md
[config.md]: https://github.com/inbucket/inbucket/blob/master/doc/config.md
[Configurator]: https://www.inbucket.org/configurator/
[CONTRIBUTING.md]: https://github.com/inbucket/inbucket/blob/develop/CONTRIBUTING.md [CONTRIBUTING.md]: https://github.com/inbucket/inbucket/blob/develop/CONTRIBUTING.md
[From Source]: http://www.inbucket.org/installation/from-source.html [Docker Image]: https://www.inbucket.org/binaries/docker.html
[Google Go]: http://golang.org/ [From Source]: https://www.inbucket.org/installation/from-source.html
[Go]: https://golang.org/
[Go API docs]: https://godoc.org/github.com/inbucket/inbucket/pkg/rest/client
[Homebrew]: http://brew.sh/ [Homebrew]: http://brew.sh/
[Homebrew Tap]: https://github.com/inbucket/homebrew-inbucket [Homebrew Tap]: https://github.com/inbucket/homebrew-inbucket
[Inbucket Website]: http://www.inbucket.org/ [Inbucket Website]: https://www.inbucket.org/
[Issues List]: https://github.com/inbucket/inbucket/issues?state=open [Issues List]: https://github.com/inbucket/inbucket/issues?state=open
[Node.js]: https://nodejs.org/en/

View File

@@ -76,8 +76,14 @@ Prior to the addition of the mailbox naming setting, Inbucket always operated in
local mode. Regardless of this setting, the `+` wildcard/extension is not local mode. Regardless of this setting, the `+` wildcard/extension is not
incorporated into the mailbox name. incorporated into the mailbox name.
#### `domain` ensures the local-part is removed, such that:
- `james@inbucket.org` is stored in `inbucket.org`
- `matt@inbucket.org` is stored in `inbucket.org`
- `matt@noinbucket.com` is stored in `notinbucket.com`
- Default: `local` - Default: `local`
- Values: one of `local` or `full` - Values: one of `local` or `full` or `domain`
## SMTP ## SMTP

23
go.mod
View File

@@ -2,17 +2,20 @@ 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/google/subcommands v0.0.0-20181012225330-46f0354f6315 github.com/go-test/deep v1.0.2 // indirect
github.com/gorilla/context v1.1.1 // indirect github.com/google/subcommands v1.0.1
github.com/gorilla/css v1.0.0 github.com/gorilla/css v1.0.0
github.com/gorilla/mux v1.6.2 github.com/gorilla/mux v1.7.3
github.com/gorilla/websocket v1.4.0 github.com/gorilla/websocket v1.4.0
github.com/jhillyerd/enmime v0.5.0 github.com/jaytaylor/html2text v0.0.0-20190408195923-01ec452cbe43 // indirect
github.com/jhillyerd/enmime v0.6.0
github.com/jhillyerd/goldiff v0.1.0 github.com/jhillyerd/goldiff v0.1.0
github.com/kelseyhightower/envconfig v1.3.0 github.com/kelseyhightower/envconfig v1.4.0
github.com/microcosm-cc/bluemonday v1.0.1 github.com/mattn/go-runewidth v0.0.4 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/microcosm-cc/bluemonday v1.0.2
github.com/rs/zerolog v1.9.1 github.com/olekukonko/tablewriter v0.0.1 // indirect
github.com/stretchr/testify v1.2.2 github.com/rs/zerolog v1.15.0
golang.org/x/net v0.0.0-20181017193950-04a2e542c03f github.com/stretchr/testify v1.3.0
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80
golang.org/x/text v0.3.2 // indirect
) )

58
go.sum
View File

@@ -1,46 +1,68 @@
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.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.1 h1:UQhStjbkDClarlmv0am7OXXO4/GaPdCGiUiMTvi28sg= github.com/go-test/deep v1.0.1 h1:UQhStjbkDClarlmv0am7OXXO4/GaPdCGiUiMTvi28sg=
github.com/go-test/deep v1.0.1/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-test/deep v1.0.1/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw=
github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/gogs/chardet v0.0.0-20150115103509-2404f7772561 h1:aBzukfDxQlCTVS0NBUjI5YA3iVeaZ9Tb5PxNrrIP1xs= github.com/gogs/chardet v0.0.0-20150115103509-2404f7772561 h1:aBzukfDxQlCTVS0NBUjI5YA3iVeaZ9Tb5PxNrrIP1xs=
github.com/gogs/chardet v0.0.0-20150115103509-2404f7772561/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14= github.com/gogs/chardet v0.0.0-20150115103509-2404f7772561/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14=
github.com/google/subcommands v0.0.0-20181012225330-46f0354f6315 h1:WW91Hq2v0qDzoPME+TPD4En72+d2Ue3ZMKPYfwR9yBU= github.com/google/subcommands v1.0.1 h1:/eqq+otEXm5vhfBrbREPCSVQbvofip6kIz+mX5TUH7k=
github.com/google/subcommands v0.0.0-20181012225330-46f0354f6315/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk= github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/jaytaylor/html2text v0.0.0-20180606194806-57d518f124b0 h1:xqgexXAGQgY3HAjNPSaCqn5Aahbo5TKsmhp8VRfr1iQ= github.com/jaytaylor/html2text v0.0.0-20180606194806-57d518f124b0 h1:xqgexXAGQgY3HAjNPSaCqn5Aahbo5TKsmhp8VRfr1iQ=
github.com/jaytaylor/html2text v0.0.0-20180606194806-57d518f124b0/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk= github.com/jaytaylor/html2text v0.0.0-20180606194806-57d518f124b0/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
github.com/jhillyerd/enmime v0.5.0 h1:0U3mdCFsFsdQcuztie8g+eQgZ2GhtVCun8AJArOqdzc= github.com/jaytaylor/html2text v0.0.0-20190408195923-01ec452cbe43 h1:jTkyeF7NZ5oIr0ESmcrpiDgAfoidCBF4F5kJhjtaRwE=
github.com/jhillyerd/enmime v0.5.0/go.mod h1:/bb6lwXIWgsVnrO4uuLg8rBVqidJYeG9I34d/WfWurg= github.com/jaytaylor/html2text v0.0.0-20190408195923-01ec452cbe43/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
github.com/jhillyerd/enmime v0.6.0 h1:FeypffI/uD1xt+Csd7gfD7mYx1h+qjgGlcI/ko5+LsI=
github.com/jhillyerd/enmime v0.6.0/go.mod h1:lwWyVhHVBdmzXx3wtRTmpIdNEJyZ85LJuVqZHVK/Rlo=
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.3.0 h1:IvRS4f2VcIQy6j4ORGIf9145T/AsUB+oY8LyvN8BXNM= github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
github.com/kelseyhightower/envconfig v1.3.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.3 h1:a+kO+98RDGEfo6asOGMmpodZq4FNtnGP54yps8BzLR4= github.com/mattn/go-runewidth v0.0.3 h1:a+kO+98RDGEfo6asOGMmpodZq4FNtnGP54yps8BzLR4=
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/microcosm-cc/bluemonday v1.0.1 h1:SIYunPjnlXcW+gVfvm0IlSeR5U3WZUOLfVmqg85Go44= github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/microcosm-cc/bluemonday v1.0.2 h1:5lPfLTTAvAbtS0VqT+94yOtFnGfUWYyx0+iToC3Os3s=
github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc=
github.com/olekukonko/tablewriter v0.0.0-20180912035003-be2c049b30cc h1:rQ1O4ZLYR2xXHXgBCCfIIGnuZ0lidMQw2S5n1oOv+Wg= github.com/olekukonko/tablewriter v0.0.0-20180912035003-be2c049b30cc h1:rQ1O4ZLYR2xXHXgBCCfIIGnuZ0lidMQw2S5n1oOv+Wg=
github.com/olekukonko/tablewriter v0.0.0-20180912035003-be2c049b30cc/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.0-20180912035003-be2c049b30cc/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= github.com/olekukonko/tablewriter v0.0.1 h1:b3iUnf1v+ppJiOfNX4yxxqfWKMQPZR5yoh8urCTFX88=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.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/rs/zerolog v1.9.1 h1:AjV/SFRF0+gEa6rSjkh0Eji/DnkrJKVpPho6SW5g4mU= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.9.1/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0 h1:uPRuwkWF4J6fGsJ2R0Gn2jB1EQiav9k3S6CSdygQJXY=
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca h1:NugYot0LIVPxTvN8n+Kvkn6TrbMyxQiuvKdEwFdR9vI= 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/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/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20181017193950-04a2e542c03f h1:4pRM7zYwpBjCnfA1jRmhItLxYJkaEnsmuAcRtA347DA= golang.org/x/net v0.0.0-20181017193950-04a2e542c03f h1:4pRM7zYwpBjCnfA1jRmhItLxYJkaEnsmuAcRtA347DA=
golang.org/x/net v0.0.0-20181017193950-04a2e542c03f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181017193950-04a2e542c03f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=

View File

@@ -38,6 +38,7 @@ const (
UnknownNaming mbNaming = iota UnknownNaming mbNaming = iota
LocalNaming LocalNaming
FullNaming FullNaming
DomainNaming
) )
// Decode a naming strategy from string. // Decode a naming strategy from string.
@@ -47,6 +48,8 @@ func (n *mbNaming) Decode(v string) error {
*n = LocalNaming *n = LocalNaming
case "full": case "full":
*n = FullNaming *n = FullNaming
case "domain":
*n = DomainNaming
default: default:
return fmt.Errorf("Unknown MailboxNaming strategy: %q", v) return fmt.Errorf("Unknown MailboxNaming strategy: %q", v)
} }
@@ -56,7 +59,7 @@ func (n *mbNaming) Decode(v string) error {
// Root contains global configuration, and structs with for specific sub-systems. // Root contains global configuration, and structs with for specific sub-systems.
type Root struct { type Root struct {
LogLevel string `required:"true" default:"info" desc:"debug, info, warn, or error"` LogLevel string `required:"true" default:"info" desc:"debug, info, warn, or error"`
MailboxNaming mbNaming `required:"true" default:"local" desc:"Use local or full addressing"` MailboxNaming mbNaming `required:"true" default:"local" desc:"Use local, full or domain addressing"`
SMTP SMTP SMTP SMTP
POP3 POP3 POP3 POP3
Web Web Web Web

View File

@@ -28,6 +28,20 @@ func (a *Addressing) ExtractMailbox(address string) (string, error) {
if a.Config.MailboxNaming == config.LocalNaming { if a.Config.MailboxNaming == config.LocalNaming {
return local, nil return local, nil
} }
if a.Config.MailboxNaming == config.DomainNaming {
// If no domain is specified, assume this is being
// used for mailbox lookup via the API.
if domain == "" {
if ValidateDomainPart(local) == false {
return "", fmt.Errorf("Domain part %q in %q failed validation", local, address)
}
return local, nil
}
if ValidateDomainPart(domain) == false {
return "", fmt.Errorf("Domain part %q in %q failed validation", domain, address)
}
return domain, nil
}
if a.Config.MailboxNaming != config.FullNaming { if a.Config.MailboxNaming != config.FullNaming {
return "", fmt.Errorf("Unknown MailboxNaming value: %v", a.Config.MailboxNaming) return "", fmt.Errorf("Unknown MailboxNaming value: %v", a.Config.MailboxNaming)
} }
@@ -128,8 +142,8 @@ func ValidateDomainPart(domain string) bool {
hasAlphaNum = true hasAlphaNum = true
labelLen++ labelLen++
case c == '-': case c == '-':
if prev == '.' { if prev == '.' || prev == '-' {
// Cannot lead with hyphen. // Cannot lead with hyphen or double hyphen.
return false return false
} }
case c == '.': case c == '.':

View File

@@ -125,111 +125,139 @@ func TestShouldStoreDomain(t *testing.T) {
func TestExtractMailboxValid(t *testing.T) { func TestExtractMailboxValid(t *testing.T) {
localPolicy := policy.Addressing{Config: &config.Root{MailboxNaming: config.LocalNaming}} localPolicy := policy.Addressing{Config: &config.Root{MailboxNaming: config.LocalNaming}}
fullPolicy := policy.Addressing{Config: &config.Root{MailboxNaming: config.FullNaming}} fullPolicy := policy.Addressing{Config: &config.Root{MailboxNaming: config.FullNaming}}
domainPolicy := policy.Addressing{Config: &config.Root{MailboxNaming: config.DomainNaming}}
testTable := []struct { testTable := []struct {
input string // Input to test input string // Input to test
local string // Expected output when mailbox naming = local local string // Expected output when mailbox naming = local
full string // Expected output when mailbox naming = full full string // Expected output when mailbox naming = full
domain string // Expected output when mailbox naming = domain
}{ }{
{ {
input: "mailbox", input: "mailbox",
local: "mailbox", local: "mailbox",
full: "mailbox", full: "mailbox",
domain: "mailbox",
}, },
{ {
input: "user123", input: "user123",
local: "user123", local: "user123",
full: "user123", full: "user123",
domain: "user123",
}, },
{ {
input: "MailBOX", input: "MailBOX",
local: "mailbox", local: "mailbox",
full: "mailbox", full: "mailbox",
domain: "mailbox",
}, },
{ {
input: "First.Last", input: "First.Last",
local: "first.last", local: "first.last",
full: "first.last", full: "first.last",
domain: "first.last",
}, },
{ {
input: "user+label", input: "user+label",
local: "user", local: "user",
full: "user", full: "user",
domain: "user",
}, },
{ {
input: "chars!#$%", input: "chars!#$%",
local: "chars!#$%", local: "chars!#$%",
full: "chars!#$%", full: "chars!#$%",
domain: "",
}, },
{ {
input: "chars&'*-", input: "chars&'*-",
local: "chars&'*-", local: "chars&'*-",
full: "chars&'*-", full: "chars&'*-",
domain: "",
}, },
{ {
input: "chars=/?^", input: "chars=/?^",
local: "chars=/?^", local: "chars=/?^",
full: "chars=/?^", full: "chars=/?^",
domain: "",
}, },
{ {
input: "chars_`.{", input: "chars_`.{",
local: "chars_`.{", local: "chars_`.{",
full: "chars_`.{", full: "chars_`.{",
domain: "",
}, },
{ {
input: "chars|}~", input: "chars|}~",
local: "chars|}~", local: "chars|}~",
full: "chars|}~", full: "chars|}~",
domain: "",
}, },
{ {
input: "mailbox@domain.com", input: "mailbox@domain.com",
local: "mailbox", local: "mailbox",
full: "mailbox@domain.com", full: "mailbox@domain.com",
domain: "domain.com",
}, },
{ {
input: "user123@domain.com", input: "user123@domain.com",
local: "user123", local: "user123",
full: "user123@domain.com", full: "user123@domain.com",
domain: "domain.com",
}, },
{ {
input: "MailBOX@domain.com", input: "MailBOX@domain.com",
local: "mailbox", local: "mailbox",
full: "mailbox@domain.com", full: "mailbox@domain.com",
domain: "domain.com",
}, },
{ {
input: "First.Last@domain.com", input: "First.Last@domain.com",
local: "first.last", local: "first.last",
full: "first.last@domain.com", full: "first.last@domain.com",
domain: "domain.com",
}, },
{ {
input: "user+label@domain.com", input: "user+label@domain.com",
local: "user", local: "user",
full: "user@domain.com", full: "user@domain.com",
domain: "domain.com",
}, },
{ {
input: "chars!#$%@domain.com", input: "chars!#$%@domain.com",
local: "chars!#$%", local: "chars!#$%",
full: "chars!#$%@domain.com", full: "chars!#$%@domain.com",
domain: "domain.com",
}, },
{ {
input: "chars&'*-@domain.com", input: "chars&'*-@domain.com",
local: "chars&'*-", local: "chars&'*-",
full: "chars&'*-@domain.com", full: "chars&'*-@domain.com",
domain: "domain.com",
}, },
{ {
input: "chars=/?^@domain.com", input: "chars=/?^@domain.com",
local: "chars=/?^", local: "chars=/?^",
full: "chars=/?^@domain.com", full: "chars=/?^@domain.com",
domain: "domain.com",
}, },
{ {
input: "chars_`.{@domain.com", input: "chars_`.{@domain.com",
local: "chars_`.{", local: "chars_`.{",
full: "chars_`.{@domain.com", full: "chars_`.{@domain.com",
domain: "domain.com",
}, },
{ {
input: "chars|}~@domain.com", input: "chars|}~@domain.com",
local: "chars|}~", local: "chars|}~",
full: "chars|}~@domain.com", full: "chars|}~@domain.com",
domain: "domain.com",
},
{
input: "chars|}~@example.co.uk",
local: "chars|}~",
full: "chars|}~@example.co.uk",
domain: "example.co.uk",
}, },
} }
for _, tc := range testTable { for _, tc := range testTable {
@@ -247,12 +275,20 @@ func TestExtractMailboxValid(t *testing.T) {
t.Errorf("Parsing %q, expected %q, got %q", tc.input, tc.full, result) t.Errorf("Parsing %q, expected %q, got %q", tc.input, tc.full, result)
} }
} }
if result, err := domainPolicy.ExtractMailbox(tc.input); tc.domain != "" && err != nil {
t.Errorf("Error while parsing with domain naming %q: %v", tc.input, err)
} else {
if result != tc.domain {
t.Errorf("Parsing %q, expected %q, got %q", tc.input, tc.domain, result)
}
}
} }
} }
func TestExtractMailboxInvalid(t *testing.T) { func TestExtractMailboxInvalid(t *testing.T) {
localPolicy := policy.Addressing{Config: &config.Root{MailboxNaming: config.LocalNaming}} localPolicy := policy.Addressing{Config: &config.Root{MailboxNaming: config.LocalNaming}}
fullPolicy := policy.Addressing{Config: &config.Root{MailboxNaming: config.FullNaming}} fullPolicy := policy.Addressing{Config: &config.Root{MailboxNaming: config.FullNaming}}
domainPolicy := policy.Addressing{Config: &config.Root{MailboxNaming: config.DomainNaming}}
// Test local mailbox naming policy. // Test local mailbox naming policy.
localInvalidTable := []struct { localInvalidTable := []struct {
input, msg string input, msg string
@@ -282,6 +318,28 @@ func TestExtractMailboxInvalid(t *testing.T) {
t.Errorf("Didn't get an error while parsing in full mode %q: %v", tt.input, tt.msg) t.Errorf("Didn't get an error while parsing in full mode %q: %v", tt.input, tt.msg)
} }
} }
// Test domain mailbox naming policy.
domainInvalidTable := []struct {
input, msg string
}{
{"", "Empty mailbox name is not permitted"},
{"user@host@domain.com", "@ symbol not permitted"},
{"first.last@dom ain.com", "Space not permitted"},
{"first\"last@domain.com", "Double quote not permitted"},
{"first\nlast@domain.com", "Control chars not permitted"},
{"first.last@chars!#$%.com", "Invalid domain name"},
{"first.last@.example.com", "Domain cannot start with dot"},
{"first.last@-example.com", "Domain canont start with dash"},
{"first.last@example.com-", "Domain cannot end with dash"},
{"first.last@example..com", "Domain cannot contain double dots"},
{"first.last@example--com", "Domain cannot contain double dashes"},
{"first.last@example.-com", "Domain cannot contain concecutive symbols"},
}
for _, tt := range domainInvalidTable {
if _, err := domainPolicy.ExtractMailbox(tt.input); err == nil {
t.Errorf("Didn't get an error while parsing in domain mode %q: %v", tt.input, tt.msg)
}
}
} }
func TestValidateDomain(t *testing.T) { func TestValidateDomain(t *testing.T) {

View File

@@ -65,14 +65,15 @@ func MailboxShowV1(w http.ResponseWriter, req *http.Request, ctx *web.Context) (
attachments := make([]*model.JSONMessageAttachmentV1, len(attachParts)) attachments := make([]*model.JSONMessageAttachmentV1, len(attachParts))
for i, part := range attachParts { for i, part := range attachParts {
content := part.Content content := part.Content
var checksum = md5.Sum(content) // Example URL: http://localhost/serve/mailbox/swaks/0001/attach/0/favicon.png
link := "http://" + req.Host + "/serve/mailbox/" + name + "/" + id + "/attach/" +
strconv.Itoa(i) + "/" + part.FileName
checksum := md5.Sum(content)
attachments[i] = &model.JSONMessageAttachmentV1{ attachments[i] = &model.JSONMessageAttachmentV1{
ContentType: part.ContentType, ContentType: part.ContentType,
FileName: part.FileName, FileName: part.FileName,
DownloadLink: "http://" + req.Host + "/mailbox/dattach/" + name + "/" + id + "/" + DownloadLink: link,
strconv.Itoa(i) + "/" + part.FileName, ViewLink: link,
ViewLink: "http://" + req.Host + "/mailbox/vattach/" + name + "/" + id + "/" +
strconv.Itoa(i) + "/" + part.FileName,
MD5: hex.EncodeToString(checksum[:]), MD5: hex.EncodeToString(checksum[:]),
} }
} }

View File

@@ -196,6 +196,10 @@ func TestRestMessage(t *testing.T) {
"From": []string{"noreply@inbucket.org"}, "From": []string{"noreply@inbucket.org"},
}, },
}, },
Attachments: []*enmime.Part{{
FileName: "favicon.png",
ContentType: "image/png",
}},
}, },
) )
mm.AddMessage("good", msg1) mm.AddMessage("good", msg1)
@@ -231,6 +235,10 @@ func TestRestMessage(t *testing.T) {
decodedStringEquals(t, result, "header/To/[0]", "fred@fish.com") decodedStringEquals(t, result, "header/To/[0]", "fred@fish.com")
decodedStringEquals(t, result, "header/To/[1]", "keyword@nsa.gov") decodedStringEquals(t, result, "header/To/[1]", "keyword@nsa.gov")
decodedStringEquals(t, result, "header/From/[0]", "noreply@inbucket.org") decodedStringEquals(t, result, "header/From/[0]", "noreply@inbucket.org")
decodedStringEquals(t, result, "attachments/[0]/filename", "favicon.png")
decodedStringEquals(t, result, "attachments/[0]/content-type", "image/png")
decodedStringEquals(t, result, "attachments/[0]/download-link", "http://localhost/serve/mailbox/good/0001/attach/0/favicon.png")
decodedStringEquals(t, result, "attachments/[0]/view-link", "http://localhost/serve/mailbox/good/0001/attach/0/favicon.png")
if t.Failed() { if t.Failed() {
// Wait for handler to finish logging // Wait for handler to finish logging

View File

@@ -346,6 +346,11 @@ func (s *Session) readyHandler(cmd string, arg string) {
s.logger.Info().Msgf("Mail from: %v", from) s.logger.Info().Msgf("Mail from: %v", from)
s.send(fmt.Sprintf("250 Roger, accepting mail from <%v>", from)) s.send(fmt.Sprintf("250 Roger, accepting mail from <%v>", from))
s.enterState(MAIL) s.enterState(MAIL)
} else if cmd == "EHLO" {
// Reset session
s.logger.Debug().Msgf("Resetting session state on EHLO request")
s.reset()
s.send("250 Session reset")
} else { } else {
s.ooSeq(cmd) s.ooSeq(cmd)
} }
@@ -394,6 +399,12 @@ func (s *Session) mailHandler(cmd string, arg string) {
} }
s.enterState(DATA) s.enterState(DATA)
return return
case "EHLO":
// Reset session
s.logger.Debug().Msgf("Resetting session state on EHLO request")
s.reset()
s.send("250 Session reset")
return
} }
s.ooSeq(cmd) s.ooSeq(cmd)
} }

View File

@@ -206,6 +206,19 @@ func TestMailState(t *testing.T) {
t.Error(err) t.Error(err)
} }
// Test late EHLO, similar to RSET
script = []scriptStep{
{"EHLO localhost", 250},
{"EHLO localhost", 250},
{"MAIL FROM:<john@gmail.com>", 250},
{"RCPT TO:<u1@gmail.com>", 250},
{"EHLO localhost", 250},
{"MAIL FROM:<john@gmail.com>", 250},
}
if err := playSession(t, server, script); err != nil {
t.Error(err)
}
// Test RSET // Test RSET
script = []scriptStep{ script = []scriptStep{
{"HELO localhost", 250}, {"HELO localhost", 250},

3770
ui/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -11,20 +11,21 @@
}, },
"dependencies": {}, "dependencies": {},
"devDependencies": { "devDependencies": {
"@babel/core": "^7.3.3", "@babel/core": "^7.5.5",
"@babel/preset-env": "^7.3.1", "@babel/preset-env": "^7.5.5",
"@fortawesome/fontawesome-free": "^5.7.2", "@fortawesome/fontawesome-free": "^5.10.1",
"@webcomponents/webcomponentsjs": "^2.2.7", "@webcomponents/webcomponentsjs": "^2.2.10",
"babel-loader": "^8.0.5", "babel-loader": "^8.0.6",
"css-loader": "^1.0.1", "css-loader": "^1.0.1",
"elm-hot-webpack-loader": "^1.0.2", "elm": "^0.19.0-no-deps",
"elm-webpack-loader": "^5.0.0", "elm-hot-webpack-loader": "^1.1.1",
"elm-webpack-loader": "^6.0.0",
"file-loader": "^3.0.1", "file-loader": "^3.0.1",
"html-webpack-plugin": "^3.2.0", "html-webpack-plugin": "^3.2.0",
"node-elm-compiler": "^5.0.1", "node-elm-compiler": "^5.0.4",
"style-loader": "^0.23.1", "style-loader": "^0.23.1",
"webpack": "^4.29.5", "webpack": "^4.39.1",
"webpack-cli": "^3.2.3", "webpack-cli": "^3.3.6",
"webpack-dev-server": "^3.2.0" "webpack-dev-server": "^3.8.0"
} }
} }