mirror of
https://github.com/jhillyerd/inbucket.git
synced 2025-12-17 09:37:02 +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 {
|
||||
Ip4address net.IP
|
||||
Ip4port int
|
||||
Domain string
|
||||
MaxIdleSeconds int
|
||||
}
|
||||
|
||||
@@ -109,6 +110,7 @@ func LoadConfig(filename string) error {
|
||||
requireOption(messages, "smtp", "store.messages")
|
||||
requireOption(messages, "pop3", "ip4.address")
|
||||
requireOption(messages, "pop3", "ip4.port")
|
||||
requireOption(messages, "pop3", "domain")
|
||||
requireOption(messages, "pop3", "max.idle.seconds")
|
||||
requireOption(messages, "web", "ip4.address")
|
||||
requireOption(messages, "web", "ip4.port")
|
||||
@@ -262,6 +264,13 @@ func parsePop3Config() error {
|
||||
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"
|
||||
pop3Config.MaxIdleSeconds, err = Config.Int(section, option)
|
||||
if err != nil {
|
||||
|
||||
@@ -53,6 +53,9 @@ 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
|
||||
|
||||
@@ -44,6 +44,22 @@ max.message.bytes=2048000
|
||||
# (for load testing): true or false
|
||||
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]
|
||||
|
||||
|
||||
@@ -44,6 +44,22 @@ max.message.bytes=2048000
|
||||
# (for load testing): true or false
|
||||
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]
|
||||
|
||||
|
||||
@@ -44,6 +44,22 @@ max.message.bytes=2048000
|
||||
# (for load testing): true or false
|
||||
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]
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/jhillyerd/inbucket/smtpd"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -46,6 +47,7 @@ var commands = map[string]bool{
|
||||
"USER": true,
|
||||
"PASS": true,
|
||||
"APOP": true,
|
||||
"CAPA": true,
|
||||
}
|
||||
|
||||
type Session struct {
|
||||
@@ -91,7 +93,8 @@ func (s *Server) startSession(id int, conn net.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
|
||||
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
|
||||
switch cmd {
|
||||
case "APOP":
|
||||
// These commands are not implemented in any state
|
||||
ses.send(fmt.Sprintf("-ERR %v command not implemented", cmd))
|
||||
ses.warn("Command %v not implemented by Inbucket", cmd)
|
||||
case "CAPA":
|
||||
// List our capabilities per RFC2449
|
||||
ses.send("+OK Capability list follows")
|
||||
ses.send("TOP")
|
||||
ses.send("USER")
|
||||
ses.send("UIDL")
|
||||
ses.send("IMPLEMENTATION Inbucket")
|
||||
ses.send(".")
|
||||
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.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:
|
||||
ses.ooSeq(cmd)
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
|
||||
// Real server code starts here
|
||||
type Server struct {
|
||||
domain string
|
||||
maxIdleSeconds int
|
||||
dataStore smtpd.DataStore
|
||||
listener net.Listener
|
||||
@@ -24,7 +25,7 @@ func New() *Server {
|
||||
// TODO is two filestores better/worse than sharing w/ smtpd?
|
||||
ds := smtpd.NewFileDataStore()
|
||||
cfg := config.GetPop3Config()
|
||||
return &Server{dataStore: ds, maxIdleSeconds: cfg.MaxIdleSeconds,
|
||||
return &Server{domain: cfg.Domain, dataStore: ds, maxIdleSeconds: cfg.MaxIdleSeconds,
|
||||
waitgroup: new(sync.WaitGroup)}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user