mirror of
https://github.com/ChronosX88/yans.git
synced 2024-11-24 20:32:19 +00:00
Implement OVER/XOVER command
This commit is contained in:
parent
6aa7450792
commit
05816654d3
@ -28,8 +28,8 @@
|
|||||||
- :heavy_check_mark: `HEAD`
|
- :heavy_check_mark: `HEAD`
|
||||||
- :heavy_check_mark: `BODY`
|
- :heavy_check_mark: `BODY`
|
||||||
- :heavy_check_mark: `STAT`
|
- :heavy_check_mark: `STAT`
|
||||||
- :x: Articles overview
|
- :construction: Articles overview
|
||||||
- :x: `OVER`
|
- :heavy_check_mark: `OVER`
|
||||||
- :x: `LIST OVERVIEW.FMT`
|
- :x: `LIST OVERVIEW.FMT`
|
||||||
- :x: `HDR`
|
- :x: `HDR`
|
||||||
- :x: `LIST HEADERS`
|
- :x: `LIST HEADERS`
|
||||||
|
@ -195,6 +195,24 @@ func (sb *SQLiteBackend) GetNextArticleByNum(g *models.Group, a *models.Article)
|
|||||||
return nextArticle, json.Unmarshal([]byte(nextArticle.HeaderRaw), &nextArticle.Header)
|
return nextArticle, json.Unmarshal([]byte(nextArticle.HeaderRaw), &nextArticle.Header)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sb *SQLiteBackend) GetArticlesByRange(g *models.Group, low, high int64) ([]models.Article, error) {
|
||||||
|
var articles []models.Article
|
||||||
|
|
||||||
|
if err := sb.db.Select(&articles, "SELECT articles.* FROM articles INNER JOIN articles_to_groups atg on atg.article_id = articles.id WHERE atg.article_number >= ? AND atg.article_number <= ? AND atg.group_id = ? ORDER BY atg.article_number", low, high, g.ID); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for i := 0; i < len(articles); i++ {
|
||||||
|
if err := sb.db.Get(&articles[i].ArticleNumber, "SELECT article_number FROM articles_to_groups WHERE article_id = ?", articles[i].ID); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal([]byte(articles[i].HeaderRaw), &articles[i].Header); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return articles, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (sb *SQLiteBackend) GetNewArticlesSince(timestamp int64) ([]string, error) {
|
func (sb *SQLiteBackend) GetNewArticlesSince(timestamp int64) ([]string, error) {
|
||||||
var articleIds []string
|
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)
|
return articleIds, sb.db.Select(&articleIds, "SELECT json_extract(articles.header, '$.Message-Id[0]') FROM articles WHERE created_at > datetime(?, 'unixepoch')", timestamp)
|
||||||
|
@ -21,4 +21,5 @@ type StorageBackend interface {
|
|||||||
GetNewArticlesSince(timestamp int64) ([]string, error)
|
GetNewArticlesSince(timestamp int64) ([]string, error)
|
||||||
GetLastArticleByNum(g *models.Group, a *models.Article) (models.Article, error)
|
GetLastArticleByNum(g *models.Group, a *models.Article) (models.Article, error)
|
||||||
GetNextArticleByNum(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)
|
||||||
}
|
}
|
||||||
|
@ -64,6 +64,8 @@ const (
|
|||||||
CommandNewNews = "NEWNEWS"
|
CommandNewNews = "NEWNEWS"
|
||||||
CommandLast = "LAST"
|
CommandLast = "LAST"
|
||||||
CommandNext = "NEXT"
|
CommandNext = "NEXT"
|
||||||
|
CommandOver = "OVER"
|
||||||
|
CommandXover = "XOVER"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -44,6 +44,8 @@ func NewHandler(b backend.StorageBackend, serverDomain string) *Handler {
|
|||||||
protocol.CommandNewNews: h.handleNewNews,
|
protocol.CommandNewNews: h.handleNewNews,
|
||||||
protocol.CommandLast: h.handleLast,
|
protocol.CommandLast: h.handleLast,
|
||||||
protocol.CommandNext: h.handleNext,
|
protocol.CommandNext: h.handleNext,
|
||||||
|
protocol.CommandOver: h.handleOver,
|
||||||
|
protocol.CommandXover: h.handleOver,
|
||||||
}
|
}
|
||||||
h.serverDomain = serverDomain
|
h.serverDomain = serverDomain
|
||||||
return h
|
return h
|
||||||
@ -300,6 +302,11 @@ func (h *Handler) handlePost(s *Session, command string, arguments []string, id
|
|||||||
// set path header
|
// set path header
|
||||||
headers.Set("Path", fmt.Sprintf("%s!not-for-mail", h.serverDomain))
|
headers.Set("Path", fmt.Sprintf("%s!not-for-mail", h.serverDomain))
|
||||||
|
|
||||||
|
// set date header
|
||||||
|
if headers.Get("Date") == "" {
|
||||||
|
headers.Set("Date", time.Now().UTC().Format(time.RFC1123Z))
|
||||||
|
}
|
||||||
|
|
||||||
headerJson, err := json.Marshal(headers)
|
headerJson, err := json.Marshal(headers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -707,6 +714,99 @@ func (h *Handler) handleNext(s *Session, command string, arguments []string, id
|
|||||||
return s.tconn.PrintfLine(protocol.NNTPResponse{Code: 223, Message: fmt.Sprintf("%d %s retrieved", a.ArticleNumber, a.Header.Get("Message-Id"))}.String())
|
return s.tconn.PrintfLine(protocol.NNTPResponse{Code: 223, Message: fmt.Sprintf("%d %s retrieved", a.ArticleNumber, a.Header.Get("Message-Id"))}.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Handler) handleOver(s *Session, command string, 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())
|
||||||
|
}
|
||||||
|
|
||||||
|
byRange := false
|
||||||
|
byNum := false
|
||||||
|
byMsgID := false
|
||||||
|
curArticle := false
|
||||||
|
|
||||||
|
if len(arguments) == 1 {
|
||||||
|
if _, _, err := utils.ParseRange(arguments[0]); err == nil {
|
||||||
|
byRange = true
|
||||||
|
} else if strings.ContainsAny(arguments[0], "<>") {
|
||||||
|
byMsgID = true
|
||||||
|
} else if _, err := strconv.Atoi(arguments[0]); err == nil {
|
||||||
|
byNum = true
|
||||||
|
}
|
||||||
|
} else if len(arguments) == 0 {
|
||||||
|
curArticle = true
|
||||||
|
} else {
|
||||||
|
return s.tconn.PrintfLine(protocol.ErrSyntaxError.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
var articles []models.Article
|
||||||
|
|
||||||
|
if byRange {
|
||||||
|
if s.currentGroup == nil {
|
||||||
|
return s.tconn.PrintfLine(protocol.NNTPResponse{Code: 412, Message: "No newsgroup selected"}.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
low, high, err := utils.ParseRange(arguments[0])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if low > high {
|
||||||
|
return s.tconn.PrintfLine(protocol.NNTPResponse{Code: 423, Message: "Empty range"}.String())
|
||||||
|
}
|
||||||
|
a, err := h.backend.GetArticlesByRange(s.currentGroup, low, high)
|
||||||
|
if err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return s.tconn.PrintfLine(protocol.NNTPResponse{Code: 423, Message: "No articles in that range"}.String())
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
articles = append(articles, a...)
|
||||||
|
} else if byMsgID {
|
||||||
|
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 with that message-id"}.String())
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
a.ArticleNumber = 0
|
||||||
|
articles = append(articles, a)
|
||||||
|
} else if byNum {
|
||||||
|
num, _ := strconv.Atoi(arguments[0])
|
||||||
|
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 such article in this group"}.String())
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
articles = append(articles, a)
|
||||||
|
} else if curArticle {
|
||||||
|
articles = append(articles, *s.currentArticle)
|
||||||
|
}
|
||||||
|
|
||||||
|
dw := s.tconn.DotWriter()
|
||||||
|
dw.Write([]byte(protocol.NNTPResponse{Code: 224, Message: "Overview information follows" + protocol.CRLF}.String()))
|
||||||
|
for _, v := range articles {
|
||||||
|
dw.Write([]byte(strconv.Itoa(v.ArticleNumber) + " "))
|
||||||
|
dw.Write([]byte(v.Header.Get("Subject") + " "))
|
||||||
|
dw.Write([]byte(v.Header.Get("From") + " "))
|
||||||
|
dw.Write([]byte(v.Header.Get("Date") + " "))
|
||||||
|
dw.Write([]byte(v.Header.Get("Message-ID") + " "))
|
||||||
|
dw.Write([]byte(v.Header.Get("References") + " "))
|
||||||
|
|
||||||
|
bytesMetadata := len([]byte(v.Body))
|
||||||
|
linesMetadata := strings.Count(v.Body, "\n")
|
||||||
|
|
||||||
|
dw.Write([]byte(strconv.Itoa(bytesMetadata) + " "))
|
||||||
|
dw.Write([]byte(strconv.Itoa(linesMetadata) + protocol.CRLF))
|
||||||
|
}
|
||||||
|
|
||||||
|
return dw.Close()
|
||||||
|
}
|
||||||
|
|
||||||
func (h *Handler) Handle(s *Session, message string, id uint) error {
|
func (h *Handler) Handle(s *Session, message string, id uint) error {
|
||||||
splittedMessage := strings.Split(message, " ")
|
splittedMessage := strings.Split(message, " ")
|
||||||
for i, v := range splittedMessage {
|
for i, v := range splittedMessage {
|
||||||
|
Loading…
Reference in New Issue
Block a user