From e6f8e39a8e9fe03793829a57c8764294916b9fb1 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Sat, 5 Feb 2022 18:42:23 +0300 Subject: [PATCH] Implement NEWNEWS command --- README.md | 4 +-- internal/backend/sqlite/sqlite.go | 5 +++ internal/backend/storage_backend.go | 1 + internal/protocol/constants.go | 1 + internal/server/handler.go | 49 +++++++++++++++++++++++++++++ 5 files changed, 58 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2dad7af..4120b78 100644 --- a/README.md +++ b/README.md @@ -43,11 +43,11 @@ - :heavy_check_mark: `LIST NEWSGROUPS` - :x: `LIST ACTIVE.TIMES` - :x: `LIST DISTRIB.PATS` -- :construction: Information Commands +- :heavy_check_mark: Information Commands - :heavy_check_mark: `DATE` - :heavy_check_mark: `HELP` - :heavy_check_mark: `NEWGROUPS` - - :x: `NEWNEWS` + - :heavy_check_mark: `NEWNEWS` ## License diff --git a/internal/backend/sqlite/sqlite.go b/internal/backend/sqlite/sqlite.go index 1f624dd..e973188 100644 --- a/internal/backend/sqlite/sqlite.go +++ b/internal/backend/sqlite/sqlite.go @@ -168,3 +168,8 @@ func (sb *SQLiteBackend) GetArticleNumbers(g *models.Group, low, high int64) ([] return numbers, nil } + +func (sb *SQLiteBackend) GetNewArticlesSince(timestamp int64) ([]string, error) { + var articleIds []string + return articleIds, sb.db.Select(&articleIds, "SELECT json_extract(articles.header, '$.Message-Id[0]') FROM articles WHERE created_at > datetime(?, 'unixepoch')", timestamp) +} diff --git a/internal/backend/storage_backend.go b/internal/backend/storage_backend.go index 008188a..19ea3a2 100644 --- a/internal/backend/storage_backend.go +++ b/internal/backend/storage_backend.go @@ -18,4 +18,5 @@ type StorageBackend interface { GetArticle(messageID string) (models.Article, error) GetArticleByNumber(g *models.Group, num int) (models.Article, error) GetArticleNumbers(g *models.Group, low, high int64) ([]int64, error) + GetNewArticlesSince(timestamp int64) ([]string, error) } diff --git a/internal/protocol/constants.go b/internal/protocol/constants.go index f530a0e..6e37c32 100644 --- a/internal/protocol/constants.go +++ b/internal/protocol/constants.go @@ -61,6 +61,7 @@ const ( CommandBody = "BODY" CommandStat = "STAT" CommandHelp = "HELP" + CommandNewNews = "NEWNEWS" ) const ( diff --git a/internal/server/handler.go b/internal/server/handler.go index d06629d..63f1360 100644 --- a/internal/server/handler.go +++ b/internal/server/handler.go @@ -41,6 +41,7 @@ func NewHandler(b backend.StorageBackend, serverDomain string) *Handler { protocol.CommandBody: h.handleArticle, protocol.CommandStat: h.handleArticle, protocol.CommandHelp: h.handleHelp, + protocol.CommandNewNews: h.handleNewNews, } h.serverDomain = serverDomain return h @@ -530,6 +531,7 @@ func (h *Handler) handleHelp(s *Session, command string, arguments []string, id " LISTGROUP [newsgroup [range]]\r\n" + " MODE READER\r\n" + " NEWGROUPS [yy]yymmdd hhmmss [GMT]\r\n" + + " NEWNEWS [yy]yymmdd hhmmss [GMT]\r\n" + " NEXT\r\n" + " POST\r\n" + " QUIT\r\n" + @@ -556,6 +558,53 @@ func (h *Handler) handleHelp(s *Session, command string, arguments []string, id return dw.Close() } +func (h *Handler) handleNewNews(s *Session, command string, 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.ErrSyntaxError.String()) + } + + dateString := arguments[0] + " " + arguments[1] + + 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.ErrSyntaxError.String()) + } + + a, err := h.backend.GetNewArticlesSince(date.Unix()) + if err != nil { + return err + } + + dw := s.tconn.DotWriter() + _, err = dw.Write([]byte(protocol.NNTPResponse{Code: 230, Message: "list of new articles by message-id follows"}.String() + protocol.CRLF)) + if err != nil { + return err + } + for _, v := range a { + _, err = dw.Write([]byte(v + protocol.CRLF)) + if err != nil { + return err + } + } + + return dw.Close() +} + func (h *Handler) Handle(s *Session, message string, id uint) error { splittedMessage := strings.Split(message, " ") for i, v := range splittedMessage {