2022-01-18 17:26:37 +00:00
|
|
|
package server
|
|
|
|
|
|
|
|
import (
|
2022-01-25 16:27:58 +00:00
|
|
|
"database/sql"
|
2022-01-18 17:26:37 +00:00
|
|
|
"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"
|
2022-01-18 17:26:37 +00:00
|
|
|
"github.com/ChronosX88/yans/internal/protocol"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Handler struct {
|
2022-01-19 21:44:27 +00:00
|
|
|
handlers map[string]func(s *Session, arguments []string, id uint) error
|
2022-01-19 19:51:08 +00:00
|
|
|
backend backend.StorageBackend
|
2022-01-18 17:26:37 +00:00
|
|
|
}
|
|
|
|
|
2022-01-19 19:51:08 +00:00
|
|
|
func NewHandler(b backend.StorageBackend) *Handler {
|
2022-01-18 17:26:37 +00:00
|
|
|
h := &Handler{}
|
2022-01-19 19:51:08 +00:00
|
|
|
h.backend = b
|
2022-01-19 21:44:27 +00:00
|
|
|
h.handlers = map[string]func(s *Session, arguments []string, id uint) error{
|
2022-01-18 17:26:37 +00:00
|
|
|
protocol.CommandCapabilities: h.handleCapabilities,
|
|
|
|
protocol.CommandDate: h.handleDate,
|
|
|
|
protocol.CommandQuit: h.handleQuit,
|
|
|
|
protocol.CommandList: h.handleList,
|
2022-01-18 17:43:05 +00:00
|
|
|
protocol.CommandMode: h.handleModeReader,
|
2022-01-25 16:27:58 +00:00
|
|
|
protocol.CommandGroup: h.handleGroup,
|
2022-01-25 21:29:30 +00:00
|
|
|
protocol.CommandNewGroups: h.handleNewGroups,
|
2022-01-18 17:26:37 +00:00
|
|
|
}
|
|
|
|
return h
|
|
|
|
}
|
|
|
|
|
2022-01-19 21:44:27 +00:00
|
|
|
func (h *Handler) handleCapabilities(s *Session, arguments []string, id uint) error {
|
|
|
|
s.tconn.StartResponse(id)
|
|
|
|
defer s.tconn.EndResponse(id)
|
2022-01-18 17:43:05 +00:00
|
|
|
return s.tconn.PrintfLine(s.capabilities.String())
|
2022-01-18 17:26:37 +00:00
|
|
|
}
|
|
|
|
|
2022-01-19 21:44:27 +00:00
|
|
|
func (h *Handler) handleDate(s *Session, arguments []string, id uint) error {
|
|
|
|
s.tconn.StartResponse(id)
|
|
|
|
defer s.tconn.EndResponse(id)
|
2022-01-18 17:26:37 +00:00
|
|
|
return s.tconn.PrintfLine("111 %s", time.Now().UTC().Format("20060102150405"))
|
|
|
|
}
|
|
|
|
|
2022-01-19 21:44:27 +00:00
|
|
|
func (h *Handler) handleQuit(s *Session, arguments []string, id uint) error {
|
2022-01-18 17:26:37 +00:00
|
|
|
s.tconn.PrintfLine(protocol.MessageNNTPServiceExitsNormally)
|
|
|
|
s.conn.Close()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-01-19 21:44:27 +00:00
|
|
|
func (h *Handler) handleList(s *Session, arguments []string, id uint) error {
|
2022-01-18 17:26:37 +00:00
|
|
|
sb := strings.Builder{}
|
|
|
|
|
|
|
|
listType := ""
|
|
|
|
if len(arguments) != 0 {
|
|
|
|
listType = arguments[0]
|
|
|
|
}
|
|
|
|
|
2022-01-19 21:44:27 +00:00
|
|
|
s.tconn.StartResponse(id)
|
|
|
|
defer s.tconn.EndResponse(id)
|
|
|
|
|
2022-01-18 17:26:37 +00:00
|
|
|
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()
|
|
|
|
}
|
|
|
|
|
2022-01-18 17:26:37 +00:00
|
|
|
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
|
2022-01-19 21:44:27 +00:00
|
|
|
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)))
|
2022-01-19 21:44:27 +00:00
|
|
|
}
|
2022-01-18 17:26:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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()
|
|
|
|
}
|
2022-01-18 17:26:37 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-01-20 21:29:58 +00:00
|
|
|
|
|
|
|
sb.Write([]byte(protocol.MessageListOfNewsgroupsFollows + protocol.CRLF))
|
2022-01-18 17:26:37 +00:00
|
|
|
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:
|
|
|
|
{
|
2022-01-18 17:43:05 +00:00
|
|
|
return s.tconn.PrintfLine(protocol.MessageSyntaxError)
|
2022-01-18 17:26:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sb.Write([]byte(protocol.MultilineEnding))
|
|
|
|
|
|
|
|
return s.tconn.PrintfLine(sb.String())
|
|
|
|
}
|
|
|
|
|
2022-01-19 21:44:27 +00:00
|
|
|
func (h *Handler) handleModeReader(s *Session, arguments []string, id uint) error {
|
2022-01-18 17:43:05 +00:00
|
|
|
if len(arguments) == 0 || arguments[0] != "READER" {
|
|
|
|
return s.tconn.PrintfLine(protocol.MessageSyntaxError)
|
|
|
|
}
|
|
|
|
|
|
|
|
(&s.capabilities).Remove(protocol.ModeReaderCapability)
|
2022-01-18 17:47:32 +00:00
|
|
|
(&s.capabilities).Remove(protocol.ListCapability)
|
2022-01-18 17:43:05 +00:00
|
|
|
(&s.capabilities).Add(protocol.Capability{Type: protocol.ReaderCapability})
|
2022-01-18 17:47:32 +00:00
|
|
|
(&s.capabilities).Add(protocol.Capability{Type: protocol.ListCapability, Params: "ACTIVE NEWSGROUPS"})
|
2022-01-18 17:43:05 +00:00
|
|
|
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)
|
2022-01-25 21:29:30 +00:00
|
|
|
} else {
|
|
|
|
return err
|
2022-01-25 16:27:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
highWaterMark, err := h.backend.GetGroupHighWaterMark(g)
|
2022-01-25 21:29:30 +00:00
|
|
|
if err != nil && err != sql.ErrNoRows {
|
2022-01-25 16:27:58 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
lowWaterMark, err := h.backend.GetGroupLowWaterMark(g)
|
2022-01-25 21:29:30 +00:00
|
|
|
if err != nil && err != sql.ErrNoRows {
|
2022-01-25 16:27:58 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
articlesCount, err := h.backend.GetArticlesCount(g)
|
2022-01-25 21:29:30 +00:00
|
|
|
if err != nil && err != sql.ErrNoRows {
|
2022-01-25 16:27:58 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
s.currentGroup = &g
|
|
|
|
|
|
|
|
return s.tconn.PrintfLine("211 %d %d %d %s", articlesCount, lowWaterMark, highWaterMark, g.GroupName)
|
|
|
|
}
|
|
|
|
|
2022-01-25 21:29:30 +00:00
|
|
|
func (h *Handler) handleNewGroups(s *Session, arguments []string, id uint) error {
|
|
|
|
s.tconn.StartResponse(id)
|
|
|
|
defer s.tconn.EndResponse(id)
|
|
|
|
|
|
|
|
if len(arguments) < 2 || len(arguments) > 3 {
|
|
|
|
return s.tconn.PrintfLine(protocol.MessageSyntaxError)
|
|
|
|
}
|
|
|
|
|
|
|
|
dateString := arguments[0] + " " + arguments[1]
|
|
|
|
//isGMT := false
|
|
|
|
//if len(arguments) == 3 {
|
|
|
|
// isGMT = true
|
|
|
|
//}
|
|
|
|
|
|
|
|
var date time.Time
|
|
|
|
|
|
|
|
var err error
|
|
|
|
if len(dateString) == 15 {
|
|
|
|
date, err = time.Parse("20060102 150405", dateString)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else if len(dateString) == 13 {
|
|
|
|
date, err = time.Parse("060102 150405", dateString)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return s.tconn.PrintfLine(protocol.MessageSyntaxError)
|
|
|
|
}
|
|
|
|
|
|
|
|
g, err := h.backend.GetNewGroupsSince(date.Unix())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var sb strings.Builder
|
|
|
|
|
|
|
|
sb.Write([]byte(protocol.MessageListOfNewsgroupsFollows + protocol.CRLF))
|
|
|
|
for _, v := range g {
|
|
|
|
// 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 {
|
|
|
|
sb.Write([]byte(fmt.Sprintf("%s 0 1 n"+protocol.CRLF, v.GroupName)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sb.Write([]byte(protocol.MultilineEnding))
|
|
|
|
|
|
|
|
return s.tconn.PrintfLine(sb.String())
|
|
|
|
}
|
|
|
|
|
2022-01-19 21:44:27 +00:00
|
|
|
func (h *Handler) Handle(s *Session, message string, id uint) error {
|
2022-01-18 17:26:37 +00:00
|
|
|
splittedMessage := strings.Split(message, " ")
|
|
|
|
for i, v := range splittedMessage {
|
|
|
|
splittedMessage[i] = strings.TrimSpace(v)
|
|
|
|
}
|
|
|
|
cmdName := splittedMessage[0]
|
|
|
|
handler, ok := h.handlers[cmdName]
|
|
|
|
if !ok {
|
2022-01-19 21:44:27 +00:00
|
|
|
s.tconn.StartResponse(id)
|
|
|
|
defer s.tconn.EndResponse(id)
|
2022-01-18 17:26:37 +00:00
|
|
|
return s.tconn.PrintfLine(protocol.MessageUnknownCommand)
|
|
|
|
}
|
2022-01-19 21:44:27 +00:00
|
|
|
return handler(s, splittedMessage[1:], id)
|
2022-01-18 17:26:37 +00:00
|
|
|
}
|