mirror of
https://github.com/ChronosX88/yans.git
synced 2024-11-23 20:12:18 +00:00
Better CAPABILITIES command implementation using declared types, use textproto package for connection i/o
This commit is contained in:
parent
719005908a
commit
834298ee93
@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"github.com/ChronosX88/yans/internal/common"
|
||||
"github.com/ChronosX88/yans/internal/config"
|
||||
"github.com/ChronosX88/yans/internal/server"
|
||||
"log"
|
||||
@ -26,7 +27,7 @@ func main() {
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt)
|
||||
|
||||
log.Println("Starting YANS...")
|
||||
log.Printf("Starting %s...", common.ServerName)
|
||||
ns, err := server.NewNNTPServer(cfg)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
@ -35,10 +36,11 @@ func main() {
|
||||
if err := ns.Start(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Println("YANS has been successfully started!")
|
||||
log.Printf("%s has been successfully started!", common.ServerName)
|
||||
log.Printf("Version: %s", common.ServerVersion)
|
||||
|
||||
for range c {
|
||||
log.Println("Stopping YANS...")
|
||||
log.Printf("Stopping %s...", common.ServerName)
|
||||
ns.Stop()
|
||||
break
|
||||
}
|
||||
|
6
internal/common/const.go
Normal file
6
internal/common/const.go
Normal file
@ -0,0 +1,6 @@
|
||||
package common
|
||||
|
||||
const (
|
||||
ServerName = "YANS"
|
||||
ServerVersion = "0.0.1"
|
||||
)
|
80
internal/protocol/capabilities.go
Normal file
80
internal/protocol/capabilities.go
Normal file
@ -0,0 +1,80 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type CapabilityType int
|
||||
|
||||
const (
|
||||
VersionCapability CapabilityType = iota
|
||||
ReaderCapability
|
||||
IHaveCapability
|
||||
PostCapability
|
||||
NewNewsCapability
|
||||
HdrCapability
|
||||
OverCapability
|
||||
ListCapability
|
||||
ImplementationCapability
|
||||
ModeReaderCapability
|
||||
)
|
||||
|
||||
func (ct CapabilityType) String() string {
|
||||
switch ct {
|
||||
case VersionCapability:
|
||||
return CapabilityNameVersion
|
||||
case ReaderCapability:
|
||||
return CapabilityNameReader
|
||||
case IHaveCapability:
|
||||
return CapabilityNameIHave
|
||||
case PostCapability:
|
||||
return CapabilityNamePost
|
||||
case NewNewsCapability:
|
||||
return CapabilityNameNewNews
|
||||
case HdrCapability:
|
||||
return CapabilityNameHdr
|
||||
case OverCapability:
|
||||
return CapabilityNameOver
|
||||
case ListCapability:
|
||||
return CapabilityNameList
|
||||
case ImplementationCapability:
|
||||
return CapabilityNameImplementation
|
||||
case ModeReaderCapability:
|
||||
return CapabilityNameModeReader
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
type Capability struct {
|
||||
Type CapabilityType
|
||||
Params string // optional
|
||||
}
|
||||
|
||||
type Capabilities []Capability
|
||||
|
||||
func (cs Capabilities) Add(c Capability) {
|
||||
for _, v := range cs {
|
||||
if v.Type == c.Type {
|
||||
return // allowed only unique items
|
||||
}
|
||||
}
|
||||
cs = append(cs, c)
|
||||
}
|
||||
|
||||
func (cs Capabilities) String() string {
|
||||
sb := strings.Builder{}
|
||||
sb.Write([]byte("101 Capability list:" + CRLF))
|
||||
|
||||
for _, v := range cs {
|
||||
if v.Params != "" {
|
||||
sb.Write([]byte(fmt.Sprintf("%s %s%s", v.Type, v.Params, CRLF)))
|
||||
} else {
|
||||
sb.Write([]byte(fmt.Sprintf("%s%s", v.Type, CRLF)))
|
||||
}
|
||||
}
|
||||
sb.Write([]byte(MultilineEnding))
|
||||
|
||||
return sb.String()
|
||||
}
|
@ -1,5 +1,10 @@
|
||||
package protocol
|
||||
|
||||
const (
|
||||
CRLF = "\r\n"
|
||||
MultilineEnding = "."
|
||||
)
|
||||
|
||||
const (
|
||||
CommandCapabilities = "CAPABILITIES"
|
||||
CommandQuit = "QUIT"
|
||||
@ -9,9 +14,23 @@ const (
|
||||
)
|
||||
|
||||
const (
|
||||
MessageNNTPServiceReadyPostingProhibited = "201 YANS NNTP Service Ready, posting prohibited\n"
|
||||
CapabilityNameVersion = "VERSION"
|
||||
CapabilityNameReader = "READER"
|
||||
CapabilityNameIHave = "IHAVE"
|
||||
CapabilityNamePost = "POST"
|
||||
CapabilityNameNewNews = "NEWNEWS"
|
||||
CapabilityNameHdr = "HDR"
|
||||
CapabilityNameOver = "OVER"
|
||||
CapabilityNameList = "LIST"
|
||||
CapabilityNameImplementation = "IMPLEMENTATION"
|
||||
CapabilityNameModeReader = "MODE-READER"
|
||||
)
|
||||
|
||||
const (
|
||||
MessageNNTPServiceReadyPostingProhibited = "201 YANS NNTP Service Ready, posting prohibited"
|
||||
MessageReaderModePostingProhibited = "201 Reader mode, posting prohibited"
|
||||
MessageNNTPServiceExitsNormally = "205 NNTP Service exits normally"
|
||||
MessageUnknownCommand = "500 Unknown command"
|
||||
MessageErrorHappened = "403 Failed to process command: "
|
||||
MessageListOfNewsgroupsFollows = "215 list of newsgroups follows"
|
||||
)
|
||||
|
@ -1,10 +1,10 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/ChronosX88/yans/internal"
|
||||
"github.com/ChronosX88/yans/internal/common"
|
||||
"github.com/ChronosX88/yans/internal/config"
|
||||
"github.com/ChronosX88/yans/internal/models"
|
||||
"github.com/ChronosX88/yans/internal/protocol"
|
||||
@ -14,10 +14,20 @@ import (
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/textproto"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
Capabilities = protocol.Capabilities{
|
||||
{Type: protocol.VersionCapability, Params: "2"},
|
||||
{Type: protocol.ImplementationCapability, Params: fmt.Sprintf("%s %s", common.ServerName, common.ServerVersion)},
|
||||
{Type: protocol.ModeReaderCapability},
|
||||
{Type: protocol.ListCapability, Params: "ACTIVE NEWSGROUPS"},
|
||||
}
|
||||
)
|
||||
|
||||
type NNTPServer struct {
|
||||
ctx context.Context
|
||||
cancelFunc context.CancelFunc
|
||||
@ -81,19 +91,21 @@ func (ns *NNTPServer) Start() error {
|
||||
}
|
||||
|
||||
func (ns *NNTPServer) handleNewConnection(ctx context.Context, conn net.Conn) {
|
||||
_, err := conn.Write([]byte(protocol.MessageNNTPServiceReadyPostingProhibited))
|
||||
_, err := conn.Write([]byte(protocol.MessageNNTPServiceReadyPostingProhibited + protocol.CRLF))
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
conn.Close()
|
||||
return
|
||||
}
|
||||
|
||||
tconn := textproto.NewConn(conn)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
break
|
||||
default:
|
||||
{
|
||||
message, err := bufio.NewReader(conn).ReadString('\n')
|
||||
message, err := tconn.ReadLine()
|
||||
if err != nil {
|
||||
if err == io.EOF || err.(*net.OpError).Unwrap() == net.ErrClosed {
|
||||
log.Printf("Client %s has diconnected!", conn.RemoteAddr().String())
|
||||
@ -104,7 +116,7 @@ func (ns *NNTPServer) handleNewConnection(ctx context.Context, conn net.Conn) {
|
||||
return
|
||||
}
|
||||
log.Printf("Received message from %s: %s", conn.RemoteAddr().String(), string(message))
|
||||
err = ns.handleMessage(conn, message)
|
||||
err = ns.handleMessage(tconn, message)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
conn.Close()
|
||||
@ -115,8 +127,7 @@ func (ns *NNTPServer) handleNewConnection(ctx context.Context, conn net.Conn) {
|
||||
}
|
||||
}
|
||||
|
||||
func (ns *NNTPServer) handleMessage(conn net.Conn, msg string) error {
|
||||
msg = strings.TrimSuffix(msg, "\r\n")
|
||||
func (ns *NNTPServer) handleMessage(conn *textproto.Conn, msg string) error {
|
||||
splittedMessage := strings.Split(msg, " ")
|
||||
command := splittedMessage[0]
|
||||
|
||||
@ -126,7 +137,7 @@ func (ns *NNTPServer) handleMessage(conn net.Conn, msg string) error {
|
||||
switch command {
|
||||
case protocol.CommandCapabilities:
|
||||
{
|
||||
reply = "101 Capability list:\r\nVERSION 2\r\nIMPLEMENTATION\r\n."
|
||||
reply = Capabilities.String()
|
||||
break
|
||||
}
|
||||
case protocol.CommandDate:
|
||||
@ -158,11 +169,11 @@ func (ns *NNTPServer) handleMessage(conn net.Conn, msg string) error {
|
||||
log.Println(err)
|
||||
}
|
||||
sb := strings.Builder{}
|
||||
sb.Write([]byte("215 list of newsgroups follows\n"))
|
||||
sb.Write([]byte(protocol.MessageListOfNewsgroupsFollows + protocol.CRLF))
|
||||
if len(splittedMessage) == 1 || splittedMessage[1] == "ACTIVE" {
|
||||
for _, v := range groups {
|
||||
// TODO set high/low mark and posting status to actual values
|
||||
sb.Write([]byte(fmt.Sprintf("%s 0 0 n\r\n", v.GroupName)))
|
||||
sb.Write([]byte(fmt.Sprintf("%s 0 0 n"+protocol.CRLF, v.GroupName)))
|
||||
}
|
||||
} else if splittedMessage[1] == "NEWSGROUPS" {
|
||||
for _, v := range groups {
|
||||
@ -189,7 +200,7 @@ func (ns *NNTPServer) handleMessage(conn net.Conn, msg string) error {
|
||||
}
|
||||
}
|
||||
|
||||
_, err := conn.Write([]byte(reply + "\r\n"))
|
||||
err := conn.PrintfLine(reply)
|
||||
if quit {
|
||||
conn.Close()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user