mirror of
https://github.com/jhillyerd/inbucket.git
synced 2025-12-17 17:47:03 +00:00
POP3 is working
Added pop3.domain config option (for APOP greeting) Implemented CAPA command Implemented APOP "encrypted" authorization Updated all sample config files to include [pop3] section Closes #8
This commit is contained in:
@@ -25,6 +25,7 @@ type SmtpConfig struct {
|
|||||||
type Pop3Config struct {
|
type Pop3Config struct {
|
||||||
Ip4address net.IP
|
Ip4address net.IP
|
||||||
Ip4port int
|
Ip4port int
|
||||||
|
Domain string
|
||||||
MaxIdleSeconds int
|
MaxIdleSeconds int
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,6 +110,7 @@ func LoadConfig(filename string) error {
|
|||||||
requireOption(messages, "smtp", "store.messages")
|
requireOption(messages, "smtp", "store.messages")
|
||||||
requireOption(messages, "pop3", "ip4.address")
|
requireOption(messages, "pop3", "ip4.address")
|
||||||
requireOption(messages, "pop3", "ip4.port")
|
requireOption(messages, "pop3", "ip4.port")
|
||||||
|
requireOption(messages, "pop3", "domain")
|
||||||
requireOption(messages, "pop3", "max.idle.seconds")
|
requireOption(messages, "pop3", "max.idle.seconds")
|
||||||
requireOption(messages, "web", "ip4.address")
|
requireOption(messages, "web", "ip4.address")
|
||||||
requireOption(messages, "web", "ip4.port")
|
requireOption(messages, "web", "ip4.port")
|
||||||
@@ -262,6 +264,13 @@ func parsePop3Config() error {
|
|||||||
return fmt.Errorf("Failed to parse [%v]%v: '%v'", section, option, err)
|
return fmt.Errorf("Failed to parse [%v]%v: '%v'", section, option, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
option = "domain"
|
||||||
|
str, err = Config.String(section, option)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to parse [%v]%v: '%v'", section, option, err)
|
||||||
|
}
|
||||||
|
pop3Config.Domain = str
|
||||||
|
|
||||||
option = "max.idle.seconds"
|
option = "max.idle.seconds"
|
||||||
pop3Config.MaxIdleSeconds, err = Config.Int(section, option)
|
pop3Config.MaxIdleSeconds, err = Config.Int(section, option)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -53,6 +53,9 @@ ip4.address=0.0.0.0
|
|||||||
# IPv4 port to listen for POP3 connections on.
|
# IPv4 port to listen for POP3 connections on.
|
||||||
ip4.port=1100
|
ip4.port=1100
|
||||||
|
|
||||||
|
# used in POP3 greeting
|
||||||
|
domain=inbucket.local
|
||||||
|
|
||||||
# How long we allow a network connection to be idle before hanging up on the
|
# How long we allow a network connection to be idle before hanging up on the
|
||||||
# client, POP3 RFC requires at least 10 minutes (600 seconds).
|
# client, POP3 RFC requires at least 10 minutes (600 seconds).
|
||||||
max.idle.seconds=600
|
max.idle.seconds=600
|
||||||
|
|||||||
@@ -44,6 +44,22 @@ max.message.bytes=2048000
|
|||||||
# (for load testing): true or false
|
# (for load testing): true or false
|
||||||
store.messages=true
|
store.messages=true
|
||||||
|
|
||||||
|
#############################################################################
|
||||||
|
[pop3]
|
||||||
|
|
||||||
|
# IPv4 address to listen for POP3 connections on.
|
||||||
|
ip4.address=0.0.0.0
|
||||||
|
|
||||||
|
# IPv4 port to listen for POP3 connections on.
|
||||||
|
ip4.port=1100
|
||||||
|
|
||||||
|
# used in POP3 greeting
|
||||||
|
domain=inbucket.local
|
||||||
|
|
||||||
|
# How long we allow a network connection to be idle before hanging up on the
|
||||||
|
# client, POP3 RFC requires at least 10 minutes (600 seconds).
|
||||||
|
max.idle.seconds=600
|
||||||
|
|
||||||
#############################################################################
|
#############################################################################
|
||||||
[web]
|
[web]
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,22 @@ max.message.bytes=2048000
|
|||||||
# (for load testing): true or false
|
# (for load testing): true or false
|
||||||
store.messages=true
|
store.messages=true
|
||||||
|
|
||||||
|
#############################################################################
|
||||||
|
[pop3]
|
||||||
|
|
||||||
|
# IPv4 address to listen for POP3 connections on.
|
||||||
|
ip4.address=0.0.0.0
|
||||||
|
|
||||||
|
# IPv4 port to listen for POP3 connections on.
|
||||||
|
ip4.port=110
|
||||||
|
|
||||||
|
# used in POP3 greeting
|
||||||
|
domain=inbucket.local
|
||||||
|
|
||||||
|
# How long we allow a network connection to be idle before hanging up on the
|
||||||
|
# client, POP3 RFC requires at least 10 minutes (600 seconds).
|
||||||
|
max.idle.seconds=600
|
||||||
|
|
||||||
#############################################################################
|
#############################################################################
|
||||||
[web]
|
[web]
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,22 @@ max.message.bytes=2048000
|
|||||||
# (for load testing): true or false
|
# (for load testing): true or false
|
||||||
store.messages=true
|
store.messages=true
|
||||||
|
|
||||||
|
#############################################################################
|
||||||
|
[pop3]
|
||||||
|
|
||||||
|
# IPv4 address to listen for POP3 connections on.
|
||||||
|
ip4.address=0.0.0.0
|
||||||
|
|
||||||
|
# IPv4 port to listen for POP3 connections on.
|
||||||
|
ip4.port=1100
|
||||||
|
|
||||||
|
# used in POP3 greeting
|
||||||
|
domain=inbucket.local
|
||||||
|
|
||||||
|
# How long we allow a network connection to be idle before hanging up on the
|
||||||
|
# client, POP3 RFC requires at least 10 minutes (600 seconds).
|
||||||
|
max.idle.seconds=600
|
||||||
|
|
||||||
#############################################################################
|
#############################################################################
|
||||||
[web]
|
[web]
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/jhillyerd/inbucket/smtpd"
|
"github.com/jhillyerd/inbucket/smtpd"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -46,6 +47,7 @@ var commands = map[string]bool{
|
|||||||
"USER": true,
|
"USER": true,
|
||||||
"PASS": true,
|
"PASS": true,
|
||||||
"APOP": true,
|
"APOP": true,
|
||||||
|
"CAPA": true,
|
||||||
}
|
}
|
||||||
|
|
||||||
type Session struct {
|
type Session struct {
|
||||||
@@ -91,7 +93,8 @@ func (s *Server) startSession(id int, conn net.Conn) {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
ses := NewSession(s, id, conn)
|
ses := NewSession(s, id, conn)
|
||||||
ses.send("+OK Inbucket POP3 server ready")
|
ses.send(fmt.Sprintf("+OK Inbucket POP3 server ready <%v.%v@%v>", os.Getpid(),
|
||||||
|
time.Now().Unix(), s.domain))
|
||||||
|
|
||||||
// This is our command reading loop
|
// This is our command reading loop
|
||||||
for ses.state != QUIT && ses.sendError == nil {
|
for ses.state != QUIT && ses.sendError == nil {
|
||||||
@@ -111,10 +114,14 @@ func (s *Server) startSession(id int, conn net.Conn) {
|
|||||||
|
|
||||||
// Commands we handle in any state
|
// Commands we handle in any state
|
||||||
switch cmd {
|
switch cmd {
|
||||||
case "APOP":
|
case "CAPA":
|
||||||
// These commands are not implemented in any state
|
// List our capabilities per RFC2449
|
||||||
ses.send(fmt.Sprintf("-ERR %v command not implemented", cmd))
|
ses.send("+OK Capability list follows")
|
||||||
ses.warn("Command %v not implemented by Inbucket", cmd)
|
ses.send("TOP")
|
||||||
|
ses.send("USER")
|
||||||
|
ses.send("UIDL")
|
||||||
|
ses.send("IMPLEMENTATION Inbucket")
|
||||||
|
ses.send(".")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,6 +198,24 @@ func (ses *Session) authorizationHandler(cmd string, args []string) {
|
|||||||
ses.send(fmt.Sprintf("+OK Found %v messages for %v", ses.msgCount, ses.user))
|
ses.send(fmt.Sprintf("+OK Found %v messages for %v", ses.msgCount, ses.user))
|
||||||
ses.enterState(TRANSACTION)
|
ses.enterState(TRANSACTION)
|
||||||
}
|
}
|
||||||
|
case "APOP":
|
||||||
|
if len(args) != 2 {
|
||||||
|
ses.warn("Expected two arguments for APOP")
|
||||||
|
ses.send("-ERR APOP requires two arguments")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ses.user = args[0]
|
||||||
|
var err error
|
||||||
|
ses.mailbox, err = ses.server.dataStore.MailboxFor(ses.user)
|
||||||
|
if err != nil {
|
||||||
|
ses.error("Failed to open mailbox for %v", ses.user)
|
||||||
|
ses.send(fmt.Sprintf("-ERR Failed to open mailbox for %v", ses.user))
|
||||||
|
ses.enterState(QUIT)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ses.loadMailbox()
|
||||||
|
ses.send(fmt.Sprintf("+OK Found %v messages for %v", ses.msgCount, ses.user))
|
||||||
|
ses.enterState(TRANSACTION)
|
||||||
default:
|
default:
|
||||||
ses.ooSeq(cmd)
|
ses.ooSeq(cmd)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,11 +12,12 @@ import (
|
|||||||
|
|
||||||
// Real server code starts here
|
// Real server code starts here
|
||||||
type Server struct {
|
type Server struct {
|
||||||
maxIdleSeconds int
|
domain string
|
||||||
dataStore smtpd.DataStore
|
maxIdleSeconds int
|
||||||
listener net.Listener
|
dataStore smtpd.DataStore
|
||||||
shutdown bool
|
listener net.Listener
|
||||||
waitgroup *sync.WaitGroup
|
shutdown bool
|
||||||
|
waitgroup *sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init a new Server object
|
// Init a new Server object
|
||||||
@@ -24,7 +25,7 @@ func New() *Server {
|
|||||||
// TODO is two filestores better/worse than sharing w/ smtpd?
|
// TODO is two filestores better/worse than sharing w/ smtpd?
|
||||||
ds := smtpd.NewFileDataStore()
|
ds := smtpd.NewFileDataStore()
|
||||||
cfg := config.GetPop3Config()
|
cfg := config.GetPop3Config()
|
||||||
return &Server{dataStore: ds, maxIdleSeconds: cfg.MaxIdleSeconds,
|
return &Server{domain: cfg.Domain, dataStore: ds, maxIdleSeconds: cfg.MaxIdleSeconds,
|
||||||
waitgroup: new(sync.WaitGroup)}
|
waitgroup: new(sync.WaitGroup)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user