From 9dec53ff1333800c281ac7b55c0dd105d49ac5e8 Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Fri, 15 Apr 2022 00:31:03 +0300 Subject: [PATCH] Implement NEWTHREADS command --- internal/backend/sqlite/sqlite.go | 6 +++++ internal/backend/storage_backend.go | 1 + internal/server/handler.go | 39 +++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+) diff --git a/internal/backend/sqlite/sqlite.go b/internal/backend/sqlite/sqlite.go index 8b88fef..067e858 100644 --- a/internal/backend/sqlite/sqlite.go +++ b/internal/backend/sqlite/sqlite.go @@ -232,3 +232,9 @@ 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) } + +func (sb *SQLiteBackend) GetNewThreads(g *models.Group, perPage int, pageNum int) ([]int, error) { + var numbers []int + + return numbers, sb.db.Select(&numbers, "SELECT atg.article_number FROM articles INNER JOIN articles_to_groups atg on atg.article_id = articles.id WHERE atg.group_id = ? AND articles.thread IS NULL ORDER BY articles.created_at DESC LIMIT ? OFFSET ?", g.ID, perPage, perPage*pageNum) +} diff --git a/internal/backend/storage_backend.go b/internal/backend/storage_backend.go index 10a7f60..634fab5 100644 --- a/internal/backend/storage_backend.go +++ b/internal/backend/storage_backend.go @@ -22,4 +22,5 @@ type StorageBackend interface { GetLastArticleByNum(g *models.Group, a *models.Article) (models.Article, error) GetNextArticleByNum(g *models.Group, a *models.Article) (models.Article, error) GetArticlesByRange(g *models.Group, low, high int64) ([]models.Article, error) + GetNewThreads(g *models.Group, perPage int, pageNum int) ([]int, error) } diff --git a/internal/server/handler.go b/internal/server/handler.go index cc58874..56f6ee0 100644 --- a/internal/server/handler.go +++ b/internal/server/handler.go @@ -50,6 +50,9 @@ func NewHandler(b backend.StorageBackend, serverDomain, uploadPath string) *Hand protocol.CommandNext: h.handleNext, protocol.CommandOver: h.handleOver, protocol.CommandXover: h.handleOver, + + // project-specific extensions + "NEWTHREADS": h.handleNewThreads, } h.serverDomain = serverDomain h.uploadPath = uploadPath @@ -865,6 +868,42 @@ func (h *Handler) handleOver(s *Session, command string, arguments []string, id return dw.Close() } +func (h *Handler) handleNewThreads(s *Session, command string, arguments []string, id uint) error { + s.tconn.StartResponse(id) + defer s.tconn.EndResponse(id) + + if s.currentGroup == nil { + return s.tconn.PrintfLine(protocol.NNTPResponse{Code: 412, Message: "no newsgroup selected"}.String()) + } + + if len(arguments) == 0 { + return s.tconn.PrintfLine(protocol.ErrSyntaxError.String()) + } + + perPage, err := strconv.Atoi(arguments[0]) + if err != nil { + return s.tconn.PrintfLine(protocol.ErrSyntaxError.String()) + } + pageNum, err := strconv.Atoi(arguments[1]) + if err != nil { + return s.tconn.PrintfLine(protocol.ErrSyntaxError.String()) + } + + threadNums, err := h.backend.GetNewThreads(s.currentGroup, perPage, pageNum) + if err != nil { + if err != sql.ErrNoRows { + return err + } + } + + dw := s.tconn.DotWriter() + dw.Write([]byte(protocol.NNTPResponse{Code: 225, Message: "New thread numbers follows" + protocol.CRLF}.String())) + for _, v := range threadNums { + dw.Write([]byte(strconv.Itoa(v) + protocol.CRLF)) + } + return dw.Close() +} + func (h *Handler) Handle(s *Session, message string, id uint) error { splittedMessage := strings.Split(message, " ") for i, v := range splittedMessage {