diff --git a/README.md b/README.md index eee4df5..bff7327 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ - :x: `IHAVE` - :construction: Article retrieving - :heavy_check_mark: `ARTICLE` - - :x: `HEAD` + - :heavy_check_mark: `HEAD` - :x: `BODY` - :x: `STAT` - :x: Articles overview diff --git a/internal/protocol/constants.go b/internal/protocol/constants.go index a1c0a6d..302980c 100644 --- a/internal/protocol/constants.go +++ b/internal/protocol/constants.go @@ -57,6 +57,7 @@ const ( CommandPost = "POST" CommandListGroup = "LISTGROUP" CommandArticle = "ARTICLE" + CommandHead = "HEAD" ) const ( diff --git a/internal/server/handler.go b/internal/server/handler.go index b203f90..aed08a2 100644 --- a/internal/server/handler.go +++ b/internal/server/handler.go @@ -36,6 +36,7 @@ func NewHandler(b backend.StorageBackend, serverDomain string) *Handler { protocol.CommandPost: h.handlePost, protocol.CommandListGroup: h.handleListgroup, protocol.CommandArticle: h.handleArticle, + protocol.CommandHead: h.handleHead, } h.serverDomain = serverDomain return h @@ -385,6 +386,7 @@ func (h *Handler) handleListgroup(s *Session, arguments []string, id uint) error return dw.Close() } +// TODO refactor this, because it's mostly duplicate of ARTICLE handler function func (h *Handler) handleArticle(s *Session, arguments []string, id uint) error { s.tconn.StartResponse(id) defer s.tconn.EndResponse(id) @@ -455,6 +457,70 @@ func (h *Handler) handleArticle(s *Session, arguments []string, id uint) error { return dw.Close() } +func (h *Handler) handleHead(s *Session, arguments []string, id uint) error { + s.tconn.StartResponse(id) + defer s.tconn.EndResponse(id) + + if len(arguments) == 0 && s.currentArticle == nil { + return s.tconn.PrintfLine(protocol.NNTPResponse{Code: 420, Message: "No current article selected"}.String()) + } + + if len(arguments) > 1 { + return s.tconn.PrintfLine(protocol.ErrSyntaxError.String()) + } + + getByArticleNum := true + num, err := strconv.Atoi(arguments[0]) + if err != nil { + getByArticleNum = false + } + + if getByArticleNum && s.currentGroup == nil { + return s.tconn.PrintfLine(protocol.NNTPResponse{Code: 412, Message: "No newsgroup selected"}.String()) + } + + var a models.Article + + if getByArticleNum { + a, err = h.backend.GetArticleByNumber(s.currentGroup, num) + if err != nil { + if err == sql.ErrNoRows { + return s.tconn.PrintfLine(protocol.NNTPResponse{Code: 423, Message: "No article with that number"}.String()) + } else { + return err + } + } + } else { + a, err = h.backend.GetArticle(arguments[0]) + if err != nil { + if err == sql.ErrNoRows { + return s.tconn.PrintfLine(protocol.NNTPResponse{Code: 430, Message: "No Such Article Found"}.String()) + } else { + return err + } + } + } + + s.currentArticle = &a + + m := utils.NewMessage() + for k, v := range a.Header { + m.SetHeader(k, v...) + } + + dw := s.tconn.DotWriter() + _, err = dw.Write([]byte(protocol.NNTPResponse{Code: 221, Message: fmt.Sprintf("%d %s", num, a.Header.Get("Message-ID"))}.String() + protocol.CRLF)) + if err != nil { + return err + } + _, err = m.WriteTo(dw) + 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 {