2022-01-18 17:26:37 +00:00
package server
import (
"context"
2022-04-13 10:10:11 +00:00
"errors"
2022-02-05 09:54:41 +00:00
"fmt"
2022-01-25 16:27:58 +00:00
"github.com/ChronosX88/yans/internal/models"
2022-01-18 17:26:37 +00:00
"github.com/ChronosX88/yans/internal/protocol"
"io"
"log"
"net"
"net/textproto"
2022-04-13 10:10:11 +00:00
"strings"
2022-01-18 17:26:37 +00:00
)
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
2022-04-13 10:10:11 +00:00
remoteAddr string
2022-01-18 17:26:37 +00:00
id string
closed chan <- bool
h * Handler
2022-01-25 16:27:58 +00:00
2022-02-05 10:51:50 +00:00
currentGroup * models . Group
currentArticle * models . Article
mode SessionMode
2022-01-18 17:26:37 +00:00
}
2022-04-13 10:10:11 +00:00
func NewSession (
ctx context . Context ,
conn net . Conn ,
remoteAddr string ,
caps protocol . Capabilities ,
id string ,
closed chan <- bool ,
handler * Handler ,
) ( * Session , error ) {
2022-01-18 17:26:37 +00:00
var err error
defer func ( ) {
if err != nil {
conn . Close ( )
close ( closed )
}
} ( )
tconn := textproto . NewConn ( conn )
s := & Session {
ctx : ctx ,
conn : conn ,
tconn : tconn ,
2022-04-13 10:10:11 +00:00
remoteAddr : remoteAddr ,
2022-01-18 17:26:37 +00:00
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-02-05 09:54:41 +00:00
err := s . tconn . PrintfLine ( protocol . NNTPResponse { Code : 201 , Message : "YANS NNTP Service Ready, posting prohibited" } . String ( ) ) // 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 {
2022-04-13 10:10:11 +00:00
if err == io . EOF || errors . Is ( err , net . ErrClosed ) || strings . Contains ( err . Error ( ) , "StatusNormalClosure" ) {
log . Printf ( "Client %s has diconnected!" , s . remoteAddr )
2022-01-18 17:26:37 +00:00
} else {
log . Print ( err )
s . conn . Close ( )
}
return
}
2022-01-19 21:44:27 +00:00
s . tconn . EndRequest ( id )
2022-04-13 10:10:11 +00:00
log . Printf ( "Received message from %s: %s" , s . remoteAddr , 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 )
2022-02-05 09:54:41 +00:00
s . tconn . PrintfLine ( protocol . NNTPResponse { Code : 403 , Message : fmt . Sprintf ( "Failed to process command: %s" , err . Error ( ) ) } . String ( ) )
2022-01-18 17:26:37 +00:00
s . conn . Close ( )
return
}
}
}
}
}