go-nntp-plusplus/examples/server/exampleserver.go

292 lines
5.7 KiB
Go
Raw Normal View History

2012-02-23 07:53:03 +00:00
package main
import (
"bytes"
"container/ring"
"io"
"log"
"net"
"net/textproto"
2012-02-23 08:25:14 +00:00
"sort"
2012-02-23 07:53:03 +00:00
"strconv"
"strings"
2023-11-12 12:48:11 +00:00
"git.maride.cc/maride/go-nntp-plusplus"
"git.maride.cc/maride/go-nntp-plusplus/server"
2012-02-23 07:53:03 +00:00
)
const maxArticles = 100
type articleRef struct {
msgid string
num int64
}
type groupStorage struct {
group *nntp.Group
// article refs
articles *ring.Ring
}
type articleStorage struct {
headers textproto.MIMEHeader
body string
refcount int
}
type testBackendType struct {
// group name -> group storage
groups map[string]*groupStorage
// message ID -> article
articles map[string]*articleStorage
}
var testBackend = testBackendType{
groups: map[string]*groupStorage{},
articles: map[string]*articleStorage{},
2012-02-23 07:53:03 +00:00
}
func init() {
testBackend.groups["alt.test"] = &groupStorage{
2013-08-01 07:59:56 +00:00
group: &nntp.Group{
Name: "alt.test",
Description: "A test.",
Posting: nntp.PostingNotPermitted},
2012-02-23 07:53:03 +00:00
articles: ring.New(maxArticles),
}
testBackend.groups["misc.test"] = &groupStorage{
2013-08-01 07:59:56 +00:00
group: &nntp.Group{
Name: "misc.test",
Description: "More testing.",
Posting: nntp.PostingPermitted},
2012-02-23 07:53:03 +00:00
articles: ring.New(maxArticles),
}
}
func (tb *testBackendType) ListGroups(max int) ([]*nntp.Group, error) {
rv := []*nntp.Group{}
2012-02-23 07:53:03 +00:00
for _, g := range tb.groups {
rv = append(rv, g.group)
}
return rv, nil
}
func (tb *testBackendType) GetGroup(name string) (*nntp.Group, error) {
var group *nntp.Group
for _, g := range tb.groups {
if g.group.Name == name {
group = g.group
break
}
}
if group == nil {
2014-02-26 06:55:09 +00:00
return nil, nntpserver.ErrNoSuchGroup
2012-02-23 07:53:03 +00:00
}
return group, nil
}
func mkArticle(a *articleStorage) *nntp.Article {
return &nntp.Article{
Header: a.headers,
Body: strings.NewReader(a.body),
Bytes: len(a.body),
Lines: strings.Count(a.body, "\n"),
}
}
func findInRing(in *ring.Ring, f func(r interface{}) bool) *ring.Ring {
if f(in.Value) {
return in
}
for p := in.Next(); p != in; p = p.Next() {
if f(p.Value) {
return p
}
}
return nil
}
func (tb *testBackendType) GetArticle(group *nntp.Group, id string) (*nntp.Article, error) {
2014-02-26 08:15:16 +00:00
msgID := id
2012-02-23 07:53:03 +00:00
var a *articleStorage
if intid, err := strconv.ParseInt(id, 10, 64); err == nil {
2014-02-26 08:15:16 +00:00
msgID = ""
2012-02-23 07:53:03 +00:00
// by int ID. Gotta go find it.
if groupStorage, ok := tb.groups[group.Name]; ok {
r := findInRing(groupStorage.articles, func(v interface{}) bool {
if v != nil {
log.Printf("Looking at %v", v)
}
if aref, ok := v.(articleRef); ok && aref.num == intid {
return true
}
return false
})
if aref, ok := r.Value.(articleRef); ok {
2014-02-26 08:15:16 +00:00
msgID = aref.msgid
2012-02-23 07:53:03 +00:00
}
}
}
2014-02-26 08:15:16 +00:00
a = tb.articles[msgID]
2012-02-23 07:53:03 +00:00
if a == nil {
2014-02-26 07:48:14 +00:00
return nil, nntpserver.ErrInvalidMessageID
2012-02-23 07:53:03 +00:00
}
return mkArticle(a), nil
}
2012-02-23 08:25:14 +00:00
// Because I suck at ring, I'm going to just post-sort these.
type nalist []nntpserver.NumberedArticle
func (n nalist) Len() int {
return len(n)
}
func (n nalist) Less(i, j int) bool {
return n[i].Num < n[j].Num
}
func (n nalist) Swap(i, j int) {
n[i], n[j] = n[j], n[i]
}
2012-02-23 07:53:03 +00:00
func (tb *testBackendType) GetArticles(group *nntp.Group,
from, to int64) ([]nntpserver.NumberedArticle, error) {
gs, ok := tb.groups[group.Name]
if !ok {
2014-02-26 07:48:14 +00:00
return nil, nntpserver.ErrNoSuchGroup
2012-02-23 07:53:03 +00:00
}
log.Printf("Getting articles from %d to %d", from, to)
rv := []nntpserver.NumberedArticle{}
2012-02-23 07:53:03 +00:00
gs.articles.Do(func(v interface{}) {
if v != nil {
if aref, ok := v.(articleRef); ok {
if aref.num >= from && aref.num <= to {
a, ok := tb.articles[aref.msgid]
if ok {
article := mkArticle(a)
2013-08-01 07:59:56 +00:00
rv = append(rv,
nntpserver.NumberedArticle{
Num: aref.num,
Article: article})
2012-02-23 07:53:03 +00:00
}
}
}
}
})
2012-02-23 08:25:14 +00:00
sort.Sort(nalist(rv))
2012-02-23 07:53:03 +00:00
return rv, nil
}
func (tb *testBackendType) AllowPost() bool {
return true
}
func (tb *testBackendType) decr(msgid string) {
if a, ok := tb.articles[msgid]; ok {
a.refcount--
if a.refcount == 0 {
log.Printf("Getting rid of %v", msgid)
delete(tb.articles, msgid)
}
}
}
func (tb *testBackendType) Post(article *nntp.Article) error {
log.Printf("Got headers: %#v", article.Header)
b := []byte{}
buf := bytes.NewBuffer(b)
n, err := io.Copy(buf, article.Body)
if err != nil {
return err
}
log.Printf("Read %d bytes of body", n)
a := articleStorage{
headers: article.Header,
body: buf.String(),
refcount: 0,
}
2014-02-26 08:15:16 +00:00
msgID := a.headers.Get("Message-Id")
2012-02-23 07:53:03 +00:00
2014-02-26 08:15:16 +00:00
if _, ok := tb.articles[msgID]; ok {
2014-02-26 07:48:14 +00:00
return nntpserver.ErrPostingFailed
2012-02-23 07:53:03 +00:00
}
for _, g := range article.Header["Newsgroups"] {
if g, ok := tb.groups[g]; ok {
g.articles = g.articles.Next()
if g.articles.Value != nil {
aref := g.articles.Value.(articleRef)
tb.decr(aref.msgid)
}
if g.articles.Value != nil || g.group.Low == 0 {
g.group.Low++
}
g.group.High++
g.articles.Value = articleRef{
2014-02-26 08:15:16 +00:00
msgID,
2012-02-23 07:53:03 +00:00
g.group.High,
}
log.Printf("Placed %v", g.articles.Value)
a.refcount++
g.group.Count = int64(g.articles.Len())
2014-02-26 08:15:16 +00:00
log.Printf("Stored %v in %v", msgID, g.group.Name)
2012-02-23 07:53:03 +00:00
}
}
if a.refcount > 0 {
2014-02-26 08:15:16 +00:00
tb.articles[msgID] = &a
2012-02-23 07:53:03 +00:00
} else {
2014-02-26 07:48:14 +00:00
return nntpserver.ErrPostingFailed
2012-02-23 07:53:03 +00:00
}
return nil
}
func (tb *testBackendType) Authorized() bool {
return true
}
func (tb *testBackendType) Authenticate(user, pass string) (nntpserver.Backend, error) {
2014-02-26 07:48:14 +00:00
return nil, nntpserver.ErrAuthRejected
2012-02-23 07:53:03 +00:00
}
func maybefatal(err error, f string, a ...interface{}) {
if err != nil {
log.Fatalf(f, a...)
}
}
func main() {
a, err := net.ResolveTCPAddr("tcp", ":1119")
maybefatal(err, "Error resolving listener: %v", err)
l, err := net.ListenTCP("tcp", a)
maybefatal(err, "Error setting up listener: %v", err)
defer l.Close()
s := nntpserver.NewServer(&testBackend)
for {
c, err := l.AcceptTCP()
maybefatal(err, "Error accepting connection: %v", err)
go s.Process(c)
}
}