2022-01-18 17:26:37 +00:00
|
|
|
package server
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"github.com/ChronosX88/yans/internal/protocol"
|
|
|
|
"io"
|
|
|
|
"log"
|
|
|
|
"net"
|
|
|
|
"net/textproto"
|
|
|
|
)
|
|
|
|
|
2022-01-18 17:43:05 +00:00
|
|
|
type SessionMode int
|
|
|
|
|
|
|
|
const (
|
|
|
|
SessionModeTransit = iota
|
|
|
|
SessionModeReader
|
|
|
|
)
|
|
|
|
|
2022-01-18 17:26:37 +00:00
|
|
|
type Session struct {
|
|
|
|
ctx context.Context
|
|
|
|
capabilities protocol.Capabilities
|
|
|
|
conn net.Conn
|
|
|
|
tconn *textproto.Conn
|
|
|
|
id string
|
|
|
|
closed chan<- bool
|
|
|
|
h *Handler
|
2022-01-18 17:43:05 +00:00
|
|
|
mode SessionMode
|
2022-01-18 17:26:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewSession(ctx context.Context, conn net.Conn, caps protocol.Capabilities, id string, closed chan<- bool, handler *Handler) (*Session, error) {
|
|
|
|
var err error
|
|
|
|
defer func() {
|
|
|
|
if err != nil {
|
|
|
|
conn.Close()
|
|
|
|
close(closed)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
tconn := textproto.NewConn(conn)
|
|
|
|
s := &Session{
|
|
|
|
ctx: ctx,
|
|
|
|
conn: conn,
|
|
|
|
tconn: tconn,
|
|
|
|
capabilities: caps,
|
|
|
|
id: id,
|
|
|
|
closed: closed,
|
|
|
|
h: handler,
|
2022-01-18 17:43:05 +00:00
|
|
|
mode: SessionModeTransit,
|
2022-01-18 17:26:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
go s.loop()
|
|
|
|
|
|
|
|
return s, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Session) loop() {
|
|
|
|
defer func() {
|
|
|
|
close(s.closed)
|
|
|
|
}()
|
|
|
|
|
2022-01-18 17:43:05 +00:00
|
|
|
err := s.tconn.PrintfLine(protocol.MessageNNTPServiceReadyPostingProhibited) // by default access mode is read-only
|
2022-01-18 17:26:37 +00:00
|
|
|
if err != nil {
|
|
|
|
s.conn.Close()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-s.ctx.Done():
|
|
|
|
break
|
|
|
|
default:
|
|
|
|
{
|
2022-01-19 21:44:27 +00:00
|
|
|
id := s.tconn.Next()
|
|
|
|
s.tconn.StartRequest(id)
|
2022-01-18 17:26:37 +00:00
|
|
|
message, err := s.tconn.ReadLine()
|
|
|
|
if err != nil {
|
|
|
|
if err == io.EOF || err.(*net.OpError).Unwrap() == net.ErrClosed {
|
|
|
|
log.Printf("Client %s has diconnected!", s.conn.RemoteAddr().String())
|
|
|
|
} else {
|
|
|
|
log.Print(err)
|
|
|
|
s.conn.Close()
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
2022-01-19 21:44:27 +00:00
|
|
|
s.tconn.EndRequest(id)
|
2022-01-18 17:26:37 +00:00
|
|
|
log.Printf("Received message from %s: %s", s.conn.RemoteAddr().String(), message) // for debugging
|
2022-01-19 21:44:27 +00:00
|
|
|
err = s.h.Handle(s, message, id)
|
2022-01-18 17:26:37 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Print(err)
|
|
|
|
s.tconn.PrintfLine("%s %s", protocol.MessageErrorHappened, err.Error())
|
|
|
|
s.conn.Close()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|