Commit Diff


commit - /dev/null
commit + c95413d6ce7857ed77579a3c8bbcce6e00d0cf92
blob - /dev/null
blob + 32e47df45d5a504f9960679933f6a37c1dc4aeae (mode 644)
--- /dev/null
+++ .gitignore
@@ -0,0 +1,3 @@
+*~
+#*
+/examples/client/client
blob - /dev/null
blob + f7fad25b82812636e323f6614970f86d1298339c (mode 644)
--- /dev/null
+++ client/client.go
@@ -0,0 +1,124 @@
+package nntpclient
+
+import (
+	"errors"
+	"io"
+	"net/textproto"
+	"strconv"
+	"strings"
+
+	"github.com/dustin/go-nntp"
+)
+
+type Client struct {
+	conn   *textproto.Conn
+	Banner string
+}
+
+func New(net, addr string) (*Client, error) {
+	conn, err := textproto.Dial(net, addr)
+	if err != nil {
+		return nil, err
+	}
+
+	_, msg, err := conn.ReadCodeLine(200)
+	if err != nil {
+		return nil, err
+	}
+
+	return &Client{
+		conn:   conn,
+		Banner: msg,
+	}, nil
+}
+
+func (c *Client) Authenticate(user, pass string) (msg string, err error) {
+	err = c.conn.PrintfLine("authinfo user %s", user)
+	if err != nil {
+		return
+	}
+	_, _, err = c.conn.ReadCodeLine(381)
+	if err != nil {
+		return
+	}
+
+	err = c.conn.PrintfLine("authinfo pass %s", pass)
+	if err != nil {
+		return
+	}
+	_, msg, err = c.conn.ReadCodeLine(281)
+	return
+}
+
+func (c *Client) Group(name string) (rv nntp.Group, err error) {
+	var msg string
+	_, msg, err = c.Command("GROUP "+name, 211)
+	if err != nil {
+		return
+	}
+	// count first last name
+	parts := strings.Split(msg, " ")
+	if len(parts) != 4 {
+		err = errors.New("Don't know how to parse result: " + msg)
+	}
+	rv.Count, err = strconv.ParseInt(parts[0], 10, 64)
+	if err != nil {
+		return
+	}
+	rv.Low, err = strconv.ParseInt(parts[1], 10, 64)
+	if err != nil {
+		return
+	}
+	rv.High, err = strconv.ParseInt(parts[2], 10, 64)
+	if err != nil {
+		return
+	}
+	rv.Name = parts[3]
+
+	return
+}
+
+func (c *Client) Article(specifier string) (int64, string, io.Reader, error) {
+	err := c.conn.PrintfLine("ARTICLE %s", specifier)
+	if err != nil {
+		return 0, "", nil, err
+	}
+	return c.articleish(220)
+}
+
+func (c *Client) Head(specifier string) (int64, string, io.Reader, error) {
+	err := c.conn.PrintfLine("HEAD %s", specifier)
+	if err != nil {
+		return 0, "", nil, err
+	}
+	return c.articleish(221)
+}
+
+func (c *Client) Body(specifier string) (int64, string, io.Reader, error) {
+	err := c.conn.PrintfLine("BODY %s", specifier)
+	if err != nil {
+		return 0, "", nil, err
+	}
+	return c.articleish(222)
+}
+
+func (c *Client) articleish(expected int) (int64, string, io.Reader, error) {
+	_, msg, err := c.conn.ReadCodeLine(expected)
+	if err != nil {
+		return 0, "", nil, err
+	}
+	parts := strings.SplitN(msg, " ", 2)
+	n, err := strconv.ParseInt(parts[0], 10, 64)
+	if err != nil {
+		return 0, "", nil, err
+	}
+	return n, parts[1], c.conn.DotReader(), nil
+}
+
+func (c *Client) Command(cmd string, expectCode int) (int, string, error) {
+	err := c.conn.PrintfLine(cmd)
+	if err != nil {
+		return 0, "", err
+	}
+	return c.conn.ReadCodeLine(expectCode)
+}
blob - /dev/null
blob + 657477b660e0d4cd844499e0883fc7df0fffa9c8 (mode 644)
--- /dev/null
+++ examples/client/exampleclient.go
@@ -0,0 +1,51 @@
+package main
+
+import (
+	"fmt"
+	"io"
+	"log"
+	"os"
+
+	"github.com/dustin/go-nntp/client"
+)
+
+func maybefatal(s string, e error) {
+	if e != nil {
+		log.Fatalf("Error in %s: %v", s, e)
+	}
+}
+
+func main() {
+	server, user, pass := os.Args[1], os.Args[2], os.Args[3]
+	c, err := nntpclient.New("tcp", server)
+	maybefatal("connecting", err)
+	log.Printf("Got banner:  %v", c.Banner)
+
+	// Set the reader mode
+	_, _, err = c.Command("mode reader", 2)
+	maybefatal("setting reader mode", err)
+
+	// Authenticate
+	msg, err := c.Authenticate(user, pass)
+	maybefatal("authenticating", err)
+	log.Printf("Post authentication message:  %v", msg)
+
+	g, err := c.Group("comp.lang.lisp")
+	maybefatal("grouping", err)
+	log.Printf("Got %#v", g)
+
+	n, id, r, err := c.Head(fmt.Sprintf("%v", g.High-1))
+	maybefatal("getting head", err)
+	log.Printf("msg %d has id %v and the following headers", n, id)
+	io.Copy(os.Stdout, r)
+
+	n, id, r, err = c.Body(fmt.Sprintf("%v", n))
+	maybefatal("getting body", err)
+	log.Printf("Body of message %v", id)
+	io.Copy(os.Stdout, r)
+
+	n, id, r, err = c.Article(fmt.Sprintf("%v", n))
+	maybefatal("getting the whole thing", err)
+	log.Printf("Full message %v", id)
+	io.Copy(os.Stdout, r)
+}
blob - /dev/null
blob + d8ec5e9ca77aa52080bf3c80ed7f36a5a6ecbae8 (mode 644)
--- /dev/null
+++ nntp.go
@@ -0,0 +1,18 @@
+package nntp
+
+type PostingStatus byte
+
+const (
+	Unknown             = PostingStatus(0)
+	PostingPermitted    = PostingStatus('y')
+	PostingNotPermitted = PostingStatus('n')
+	PostingModerated    = PostingStatus('m')
+)
+
+type Group struct {
+	Name    string
+	Count   int64
+	High    int64
+	Low     int64
+	Posting PostingStatus
+}