yans/internal/server/handler.go

195 lines
5.1 KiB
Go
Raw Normal View History

package server
import (
2022-01-25 16:27:58 +00:00
"database/sql"
"fmt"
2022-01-19 19:51:08 +00:00
"github.com/ChronosX88/yans/internal/backend"
2022-01-20 21:29:58 +00:00
"github.com/ChronosX88/yans/internal/models"
"github.com/ChronosX88/yans/internal/protocol"
"strings"
"time"
)
type Handler struct {
handlers map[string]func(s *Session, arguments []string, id uint) error
2022-01-19 19:51:08 +00:00
backend backend.StorageBackend
}
2022-01-19 19:51:08 +00:00
func NewHandler(b backend.StorageBackend) *Handler {
h := &Handler{}
2022-01-19 19:51:08 +00:00
h.backend = b
h.handlers = map[string]func(s *Session, arguments []string, id uint) error{
protocol.CommandCapabilities: h.handleCapabilities,
protocol.CommandDate: h.handleDate,
protocol.CommandQuit: h.handleQuit,
protocol.CommandList: h.handleList,
protocol.CommandMode: h.handleModeReader,
2022-01-25 16:27:58 +00:00
protocol.CommandGroup: h.handleGroup,
}
return h
}
func (h *Handler) handleCapabilities(s *Session, arguments []string, id uint) error {
s.tconn.StartResponse(id)
defer s.tconn.EndResponse(id)
return s.tconn.PrintfLine(s.capabilities.String())
}
func (h *Handler) handleDate(s *Session, arguments []string, id uint) error {
s.tconn.StartResponse(id)
defer s.tconn.EndResponse(id)
return s.tconn.PrintfLine("111 %s", time.Now().UTC().Format("20060102150405"))
}
func (h *Handler) handleQuit(s *Session, arguments []string, id uint) error {
s.tconn.PrintfLine(protocol.MessageNNTPServiceExitsNormally)
s.conn.Close()
return nil
}
func (h *Handler) handleList(s *Session, arguments []string, id uint) error {
sb := strings.Builder{}
listType := ""
if len(arguments) != 0 {
listType = arguments[0]
}
s.tconn.StartResponse(id)
defer s.tconn.EndResponse(id)
switch listType {
case "":
fallthrough
case "ACTIVE":
{
2022-01-20 21:29:58 +00:00
var groups []models.Group
var err error
if len(arguments) == 2 {
groups, err = h.backend.ListGroupsByPattern(arguments[1])
} else {
groups, err = h.backend.ListGroups()
}
if err != nil {
return err
}
sb.Write([]byte(protocol.MessageListOfNewsgroupsFollows + protocol.CRLF))
for _, v := range groups {
2022-01-20 21:29:58 +00:00
// TODO set actual post permission status
c, err := h.backend.GetArticlesCount(v)
if err != nil {
return err
}
if c > 0 {
highWaterMark, err := h.backend.GetGroupHighWaterMark(v)
if err != nil {
return err
}
lowWaterMark, err := h.backend.GetGroupLowWaterMark(v)
if err != nil {
return err
}
sb.Write([]byte(fmt.Sprintf("%s %d %d n"+protocol.CRLF, v.GroupName, highWaterMark, lowWaterMark)))
} else {
2022-01-20 21:29:58 +00:00
sb.Write([]byte(fmt.Sprintf("%s 0 1 n"+protocol.CRLF, v.GroupName)))
}
}
}
case "NEWSGROUPS":
{
2022-01-20 21:29:58 +00:00
var groups []models.Group
var err error
if len(arguments) == 2 {
groups, err = h.backend.ListGroupsByPattern(arguments[1])
} else {
groups, err = h.backend.ListGroups()
}
if err != nil {
return err
}
2022-01-20 21:29:58 +00:00
sb.Write([]byte(protocol.MessageListOfNewsgroupsFollows + protocol.CRLF))
for _, v := range groups {
desc := ""
if v.Description == nil {
desc = "No description"
} else {
desc = *v.Description
}
sb.Write([]byte(fmt.Sprintf("%s %s"+protocol.CRLF, v.GroupName, desc)))
}
}
default:
{
return s.tconn.PrintfLine(protocol.MessageSyntaxError)
}
}
sb.Write([]byte(protocol.MultilineEnding))
return s.tconn.PrintfLine(sb.String())
}
func (h *Handler) handleModeReader(s *Session, arguments []string, id uint) error {
if len(arguments) == 0 || arguments[0] != "READER" {
return s.tconn.PrintfLine(protocol.MessageSyntaxError)
}
(&s.capabilities).Remove(protocol.ModeReaderCapability)
(&s.capabilities).Remove(protocol.ListCapability)
(&s.capabilities).Add(protocol.Capability{Type: protocol.ReaderCapability})
(&s.capabilities).Add(protocol.Capability{Type: protocol.ListCapability, Params: "ACTIVE NEWSGROUPS"})
s.mode = SessionModeReader
return s.tconn.PrintfLine(protocol.MessageReaderModePostingProhibited) // TODO vary on auth status
}
2022-01-25 16:27:58 +00:00
func (h *Handler) handleGroup(s *Session, arguments []string, id uint) error {
s.tconn.StartResponse(id)
defer s.tconn.EndResponse(id)
if len(arguments) == 0 || len(arguments) > 1 {
return s.tconn.PrintfLine(protocol.MessageSyntaxError)
}
g, err := h.backend.GetGroup(arguments[0])
if err != nil {
if err == sql.ErrNoRows {
return s.tconn.PrintfLine(protocol.MessageNoSuchGroup)
}
return err
}
highWaterMark, err := h.backend.GetGroupHighWaterMark(g)
if err != nil {
return err
}
lowWaterMark, err := h.backend.GetGroupLowWaterMark(g)
if err != nil {
return err
}
articlesCount, err := h.backend.GetArticlesCount(g)
if err != nil {
return err
}
s.currentGroup = &g
return s.tconn.PrintfLine("211 %d %d %d %s", articlesCount, lowWaterMark, highWaterMark, g.GroupName)
}
func (h *Handler) Handle(s *Session, message string, id uint) error {
splittedMessage := strings.Split(message, " ")
for i, v := range splittedMessage {
splittedMessage[i] = strings.TrimSpace(v)
}
cmdName := splittedMessage[0]
handler, ok := h.handlers[cmdName]
if !ok {
s.tconn.StartResponse(id)
defer s.tconn.EndResponse(id)
return s.tconn.PrintfLine(protocol.MessageUnknownCommand)
}
return handler(s, splittedMessage[1:], id)
}