aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNigel Tao <nigeltao@golang.org>2010-10-20 09:43:27 +1100
committerNigel Tao <nigeltao@golang.org>2010-10-20 09:43:27 +1100
commita3bcf4b6640450ab249cbd4e074a05c274812bc7 (patch)
treef2d4b3196543b3a66a2e50196196b0ef14a49c3e
parentd5dffb9f29ab9548ef1e8f03e8120f9fb611781f (diff)
downloadgo-a3bcf4b6640450ab249cbd4e074a05c274812bc7.tar.gz
go-a3bcf4b6640450ab249cbd4e074a05c274812bc7.zip
nntp: remove nntp package from the standard library.
R=adg CC=golang-dev https://golang.org/cl/2470045
-rw-r--r--src/pkg/Makefile1
-rw-r--r--src/pkg/nntp/Makefile11
-rw-r--r--src/pkg/nntp/nntp.go707
-rw-r--r--src/pkg/nntp/nntp_test.go240
4 files changed, 0 insertions, 959 deletions
diff --git a/src/pkg/Makefile b/src/pkg/Makefile
index 3371fe82d0..404deb05b9 100644
--- a/src/pkg/Makefile
+++ b/src/pkg/Makefile
@@ -96,7 +96,6 @@ DIRS=\
net/dict\
net/textproto\
netchan\
- nntp\
os\
os/signal\
patch\
diff --git a/src/pkg/nntp/Makefile b/src/pkg/nntp/Makefile
deleted file mode 100644
index ce5e447555..0000000000
--- a/src/pkg/nntp/Makefile
+++ /dev/null
@@ -1,11 +0,0 @@
-# Copyright 2009 The Go Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file.
-
-include ../../Make.inc
-
-TARG=nntp
-GOFILES=\
- nntp.go
-
-include ../../Make.pkg
diff --git a/src/pkg/nntp/nntp.go b/src/pkg/nntp/nntp.go
deleted file mode 100644
index ce7a2ccd2d..0000000000
--- a/src/pkg/nntp/nntp.go
+++ /dev/null
@@ -1,707 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// The nntp package implements a client for the news protocol NNTP,
-// as defined in RFC 3977.
-package nntp
-
-import (
- "bufio"
- "bytes"
- "container/vector"
- "fmt"
- "http"
- "io"
- "io/ioutil"
- "os"
- "net"
- "sort"
- "strconv"
- "strings"
- "time"
-)
-
-// timeFormatNew is the NNTP time format string for NEWNEWS / NEWGROUPS
-const timeFormatNew = "20060102 150405"
-
-// timeFormatDate is the NNTP time format string for responses to the DATE command
-const timeFormatDate = "20060102150405"
-
-// An Error represents an error response from an NNTP server.
-type Error struct {
- Code uint
- Msg string
-}
-
-// A ProtocolError represents responses from an NNTP server
-// that seem incorrect for NNTP.
-type ProtocolError string
-
-// A Conn represents a connection to an NNTP server. The connection with
-// an NNTP server is stateful; it keeps track of what group you have
-// selected, if any, and (if you have a group selected) which article is
-// current, next, or previous.
-//
-// Some methods that return information about a specific message take
-// either a message-id, which is global across all NNTP servers, groups,
-// and messages, or a message-number, which is an integer number that is
-// local to the NNTP session and currently selected group.
-//
-// For all methods that return an io.Reader (or an *Article, which contains
-// an io.Reader), that io.Reader is only valid until the next call to a
-// method of Conn.
-type Conn struct {
- conn io.WriteCloser
- r *bufio.Reader
- br *bodyReader
- close bool
-}
-
-// A Group gives information about a single news group on the server.
-type Group struct {
- Name string
- // High and low message-numbers
- High, Low int
- // Status indicates if general posting is allowed --
- // typical values are "y", "n", or "m".
- Status string
-}
-
-// An Article represents an NNTP article.
-type Article struct {
- Header map[string][]string
- Body io.Reader
-}
-
-// A bodyReader satisfies reads by reading from the connection
-// until it finds a line containing just .
-type bodyReader struct {
- c *Conn
- eof bool
- buf *bytes.Buffer
-}
-
-var dotnl = []byte(".\n")
-var dotdot = []byte("..")
-
-func (r *bodyReader) Read(p []byte) (n int, err os.Error) {
- if r.eof {
- return 0, os.EOF
- }
- if r.buf == nil {
- r.buf = &bytes.Buffer{}
- }
- if r.buf.Len() == 0 {
- b, err := r.c.r.ReadBytes('\n')
- if err != nil {
- return 0, err
- }
- // canonicalize newlines
- if b[len(b)-2] == '\r' { // crlf->lf
- b = b[0 : len(b)-1]
- b[len(b)-1] = '\n'
- }
- // stop on .
- if bytes.Equal(b, dotnl) {
- r.eof = true
- return 0, os.EOF
- }
- // unescape leading ..
- if bytes.HasPrefix(b, dotdot) {
- b = b[1:]
- }
- r.buf.Write(b)
- }
- n, _ = r.buf.Read(p)
- return
-}
-
-func (r *bodyReader) discard() os.Error {
- _, err := ioutil.ReadAll(r)
- return err
-}
-
-// articleReader satisfies reads by dumping out an article's headers
-// and body.
-type articleReader struct {
- a *Article
- headerdone bool
- headerbuf *bytes.Buffer
-}
-
-func (r *articleReader) Read(p []byte) (n int, err os.Error) {
- if r.headerbuf == nil {
- buf := new(bytes.Buffer)
- for k, fv := range r.a.Header {
- for _, v := range fv {
- fmt.Fprintf(buf, "%s: %s\n", k, v)
- }
- }
- if r.a.Body != nil {
- fmt.Fprintf(buf, "\n")
- }
- r.headerbuf = buf
- }
- if !r.headerdone {
- n, err = r.headerbuf.Read(p)
- if err == os.EOF {
- err = nil
- r.headerdone = true
- }
- if n > 0 {
- return
- }
- }
- if r.a.Body != nil {
- n, err = r.a.Body.Read(p)
- if err == os.EOF {
- r.a.Body = nil
- }
- return
- }
- return 0, os.EOF
-}
-
-func (a *Article) String() string {
- id, ok := a.Header["Message-Id"]
- if !ok {
- return "[NNTP article]"
- }
- return fmt.Sprintf("[NNTP article %s]", id[0])
-}
-
-func (a *Article) WriteTo(w io.Writer) (int64, os.Error) {
- return io.Copy(w, &articleReader{a: a})
-}
-
-func (p ProtocolError) String() string {
- return string(p)
-}
-
-func (e Error) String() string {
- return fmt.Sprintf("%03d %s", e.Code, e.Msg)
-}
-
-func maybeId(cmd, id string) string {
- if len(id) > 0 {
- return cmd + " " + id
- }
- return cmd
-}
-
-// Dial connects to an NNTP server.
-// The network and addr are passed to net.Dial to
-// make the connection.
-//
-// Example:
-// conn, err := nntp.Dial("tcp", "my.news:nntp")
-//
-func Dial(network, addr string) (*Conn, os.Error) {
- res := new(Conn)
- c, err := net.Dial(network, "", addr)
- if err != nil {
- return nil, err
- }
-
- res.conn = c
- if res.r, err = bufio.NewReaderSize(c, 4096); err != nil {
- return nil, err
- }
-
- _, err = res.r.ReadString('\n')
- if err != nil {
- return nil, err
- }
-
- return res, nil
-}
-
-func (c *Conn) body() io.Reader {
- c.br = &bodyReader{c: c}
- return c.br
-}
-
-// readStrings reads a list of strings from the NNTP connection,
-// stopping at a line containing only a . (Convenience method for
-// LIST, etc.)
-func (c *Conn) readStrings() ([]string, os.Error) {
- var sv vector.StringVector
- for {
- line, err := c.r.ReadString('\n')
- if err != nil {
- return nil, err
- }
- if strings.HasSuffix(line, "\r\n") {
- line = line[0 : len(line)-2]
- } else if strings.HasSuffix(line, "\n") {
- line = line[0 : len(line)-1]
- }
- if line == "." {
- break
- }
- sv.Push(line)
- }
- return []string(sv), nil
-}
-
-// Authenticate logs in to the NNTP server.
-// It only sends the password if the server requires one.
-func (c *Conn) Authenticate(username, password string) os.Error {
- code, _, err := c.cmd(2, "AUTHINFO USER %s", username)
- if code/100 == 3 {
- _, _, err = c.cmd(2, "AUTHINFO PASS %s", password)
- }
- return err
-}
-
-// cmd executes an NNTP command:
-// It sends the command given by the format and arguments, and then
-// reads the response line. If expectCode > 0, the status code on the
-// response line must match it. 1 digit expectCodes only check the first
-// digit of the status code, etc.
-func (c *Conn) cmd(expectCode uint, format string, args ...interface{}) (code uint, line string, err os.Error) {
- if c.close {
- return 0, "", ProtocolError("connection closed")
- }
- if c.br != nil {
- if err := c.br.discard(); err != nil {
- return 0, "", err
- }
- c.br = nil
- }
- if _, err := fmt.Fprintf(c.conn, format+"\r\n", args...); err != nil {
- return 0, "", err
- }
- line, err = c.r.ReadString('\n')
- if err != nil {
- return 0, "", err
- }
- line = strings.TrimSpace(line)
- if len(line) < 4 || line[3] != ' ' {
- return 0, "", ProtocolError("short response: " + line)
- }
- code, err = strconv.Atoui(line[0:3])
- if err != nil {
- return 0, "", ProtocolError("invalid response code: " + line)
- }
- line = line[4:]
- if 1 <= expectCode && expectCode < 10 && code/100 != expectCode ||
- 10 <= expectCode && expectCode < 100 && code/10 != expectCode ||
- 100 <= expectCode && expectCode < 1000 && code != expectCode {
- err = Error{code, line}
- }
- return
-}
-
-// ModeReader switches the NNTP server to "reader" mode, if it
-// is a mode-switching server.
-func (c *Conn) ModeReader() os.Error {
- _, _, err := c.cmd(20, "MODE READER")
- return err
-}
-
-// NewGroups returns a list of groups added since the given time.
-func (c *Conn) NewGroups(since *time.Time) ([]Group, os.Error) {
- if _, _, err := c.cmd(231, "NEWGROUPS %s GMT", since.Format(timeFormatNew)); err != nil {
- return nil, err
- }
- return c.readGroups()
-}
-
-func (c *Conn) readGroups() ([]Group, os.Error) {
- lines, err := c.readStrings()
- if err != nil {
- return nil, err
- }
- return parseGroups(lines)
-}
-
-// NewNews returns a list of the IDs of articles posted
-// to the given group since the given time.
-func (c *Conn) NewNews(group string, since *time.Time) ([]string, os.Error) {
- if _, _, err := c.cmd(230, "NEWNEWS %s %s GMT", group, since.Format(timeFormatNew)); err != nil {
- return nil, err
- }
-
- id, err := c.readStrings()
- if err != nil {
- return nil, err
- }
-
- sort.SortStrings(id)
- w := 0
- for r, s := range id {
- if r == 0 || id[r-1] != s {
- id[w] = s
- w++
- }
- }
- id = id[0:w]
-
- return id, nil
-}
-
-// parseGroups is used to parse a list of group states.
-func parseGroups(lines []string) ([]Group, os.Error) {
- var res vector.Vector
- for _, line := range lines {
- ss := strings.Split(strings.TrimSpace(line), " ", 4)
- if len(ss) < 4 {
- return nil, ProtocolError("short group info line: " + line)
- }
- high, err := strconv.Atoi(ss[1])
- if err != nil {
- return nil, ProtocolError("bad number in line: " + line)
- }
- low, err := strconv.Atoi(ss[2])
- if err != nil {
- return nil, ProtocolError("bad number in line: " + line)
- }
- res.Push(&Group{ss[0], high, low, ss[3]})
- }
- realres := make([]Group, res.Len())
- for i, v := range res {
- realres[i] = *v.(*Group)
- }
- return realres, nil
-}
-
-// Capabilities returns a list of features this server performs.
-// Not all servers support capabilities.
-func (c *Conn) Capabilities() ([]string, os.Error) {
- if _, _, err := c.cmd(101, "CAPABILITIES"); err != nil {
- return nil, err
- }
- return c.readStrings()
-}
-
-// Date returns the current time on the server.
-// Typically the time is later passed to NewGroups or NewNews.
-func (c *Conn) Date() (*time.Time, os.Error) {
- _, line, err := c.cmd(111, "DATE")
- if err != nil {
- return nil, err
- }
- t, err := time.Parse(timeFormatDate, line)
- if err != nil {
- return nil, ProtocolError("invalid time: " + line)
- }
- return t, nil
-}
-
-// List returns a list of groups present on the server.
-// Valid forms are:
-//
-// List() - return active groups
-// List(keyword) - return different kinds of information about groups
-// List(keyword, pattern) - filter groups against a glob-like pattern called a wildmat
-//
-func (c *Conn) List(a ...string) ([]string, os.Error) {
- if len(a) > 2 {
- return nil, ProtocolError("List only takes up to 2 arguments")
- }
- cmd := "LIST"
- if len(a) > 0 {
- cmd += " " + a[0]
- if len(a) > 1 {
- cmd += " " + a[1]
- }
- }
- if _, _, err := c.cmd(215, cmd); err != nil {
- return nil, err
- }
- return c.readStrings()
-}
-
-// Group changes the current group.
-func (c *Conn) Group(group string) (number, low, high int, err os.Error) {
- _, line, err := c.cmd(211, "GROUP %s", group)
- if err != nil {
- return
- }
-
- ss := strings.Split(line, " ", 4) // intentional -- we ignore optional message
- if len(ss) < 3 {
- err = ProtocolError("bad group response: " + line)
- return
- }
-
- var n [3]int
- for i, _ := range n {
- c, err := strconv.Atoi(ss[i])
- if err != nil {
- err = ProtocolError("bad group response: " + line)
- return
- }
- n[i] = c
- }
- number, low, high = n[0], n[1], n[2]
- return
-}
-
-// Help returns the server's help text.
-func (c *Conn) Help() (io.Reader, os.Error) {
- if _, _, err := c.cmd(100, "HELP"); err != nil {
- return nil, err
- }
- return c.body(), nil
-}
-
-// nextLastStat performs the work for NEXT, LAST, and STAT.
-func (c *Conn) nextLastStat(cmd, id string) (string, string, os.Error) {
- _, line, err := c.cmd(223, maybeId(cmd, id))
- if err != nil {
- return "", "", err
- }
- ss := strings.Split(line, " ", 3) // optional comment ignored
- if len(ss) < 2 {
- return "", "", ProtocolError("Bad response to " + cmd + ": " + line)
- }
- return ss[0], ss[1], nil
-}
-
-// Stat looks up the message with the given id and returns its
-// message number in the current group, and vice versa.
-// The returned message number can be "0" if the current group
-// isn't one of the groups the message was posted to.
-func (c *Conn) Stat(id string) (number, msgid string, err os.Error) {
- return c.nextLastStat("STAT", id)
-}
-
-// Last selects the previous article, returning its message number and id.
-func (c *Conn) Last() (number, msgid string, err os.Error) {
- return c.nextLastStat("LAST", "")
-}
-
-// Next selects the next article, returning its message number and id.
-func (c *Conn) Next() (number, msgid string, err os.Error) {
- return c.nextLastStat("NEXT", "")
-}
-
-// ArticleText returns the article named by id as an io.Reader.
-// The article is in plain text format, not NNTP wire format.
-func (c *Conn) ArticleText(id string) (io.Reader, os.Error) {
- if _, _, err := c.cmd(220, maybeId("ARTICLE", id)); err != nil {
- return nil, err
- }
- return c.body(), nil
-}
-
-// Article returns the article named by id as an *Article.
-func (c *Conn) Article(id string) (*Article, os.Error) {
- if _, _, err := c.cmd(220, maybeId("ARTICLE", id)); err != nil {
- return nil, err
- }
- r := bufio.NewReader(c.body())
- res, err := c.readHeader(r)
- if err != nil {
- return nil, err
- }
- res.Body = r
- return res, nil
-}
-
-// HeadText returns the header for the article named by id as an io.Reader.
-// The article is in plain text format, not NNTP wire format.
-func (c *Conn) HeadText(id string) (io.Reader, os.Error) {
- if _, _, err := c.cmd(221, maybeId("HEAD", id)); err != nil {
- return nil, err
- }
- return c.body(), nil
-}
-
-// Head returns the header for the article named by id as an *Article.
-// The Body field in the Article is nil.
-func (c *Conn) Head(id string) (*Article, os.Error) {
- if _, _, err := c.cmd(221, maybeId("HEAD", id)); err != nil {
- return nil, err
- }
- return c.readHeader(bufio.NewReader(c.body()))
-}
-
-// Body returns the body for the article named by id as an io.Reader.
-func (c *Conn) Body(id string) (io.Reader, os.Error) {
- if _, _, err := c.cmd(222, maybeId("BODY", id)); err != nil {
- return nil, err
- }
- return c.body(), nil
-}
-
-// RawPost reads a text-formatted article from r and posts it to the server.
-func (c *Conn) RawPost(r io.Reader) os.Error {
- if _, _, err := c.cmd(3, "POST"); err != nil {
- return err
- }
- br := bufio.NewReader(r)
- eof := false
- for {
- line, err := br.ReadString('\n')
- if err == os.EOF {
- eof = true
- } else if err != nil {
- return err
- }
- if eof && len(line) == 0 {
- break
- }
- if strings.HasSuffix(line, "\n") {
- line = line[0 : len(line)-1]
- }
- var prefix string
- if strings.HasPrefix(line, ".") {
- prefix = "."
- }
- _, err = fmt.Fprintf(c.conn, "%s%s\r\n", prefix, line)
- if err != nil {
- return err
- }
- if eof {
- break
- }
- }
-
- if _, _, err := c.cmd(240, "."); err != nil {
- return err
- }
- return nil
-}
-
-// Post posts an article to the server.
-func (c *Conn) Post(a *Article) os.Error {
- return c.RawPost(&articleReader{a: a})
-}
-
-// Quit sends the QUIT command and closes the connection to the server.
-func (c *Conn) Quit() os.Error {
- _, _, err := c.cmd(0, "QUIT")
- c.conn.Close()
- c.close = true
- return err
-}
-
-// Functions after this point are mostly copy-pasted from http
-// (though with some modifications). They should be factored out to
-// a common library.
-
-// Read a line of bytes (up to \n) from b.
-// Give up if the line exceeds maxLineLength.
-// The returned bytes are a pointer into storage in
-// the bufio, so they are only valid until the next bufio read.
-func readLineBytes(b *bufio.Reader) (p []byte, err os.Error) {
- if p, err = b.ReadSlice('\n'); err != nil {
- // We always know when EOF is coming.
- // If the caller asked for a line, there should be a line.
- if err == os.EOF {
- err = io.ErrUnexpectedEOF
- }
- return nil, err
- }
-
- // Chop off trailing white space.
- var i int
- for i = len(p); i > 0; i-- {
- if c := p[i-1]; c != ' ' && c != '\r' && c != '\t' && c != '\n' {
- break
- }
- }
- return p[0:i], nil
-}
-
-var colon = []byte{':'}
-
-// Read a key/value pair from b.
-// A key/value has the form Key: Value\r\n
-// and the Value can continue on multiple lines if each continuation line
-// starts with a space/tab.
-func readKeyValue(b *bufio.Reader) (key, value string, err os.Error) {
- line, e := readLineBytes(b)
- if e == io.ErrUnexpectedEOF {
- return "", "", nil
- } else if e != nil {
- return "", "", e
- }
- if len(line) == 0 {
- return "", "", nil
- }
-
- // Scan first line for colon.
- i := bytes.Index(line, colon)
- if i < 0 {
- goto Malformed
- }
-
- key = string(line[0:i])
- if strings.Index(key, " ") >= 0 {
- // Key field has space - no good.
- goto Malformed
- }
-
- // Skip initial space before value.
- for i++; i < len(line); i++ {
- if line[i] != ' ' && line[i] != '\t' {
- break
- }
- }
- value = string(line[i:])
-
- // Look for extension lines, which must begin with space.
- for {
- c, e := b.ReadByte()
- if c != ' ' && c != '\t' {
- if e != os.EOF {
- b.UnreadByte()
- }
- break
- }
-
- // Eat leading space.
- for c == ' ' || c == '\t' {
- if c, e = b.ReadByte(); e != nil {
- if e == os.EOF {
- e = io.ErrUnexpectedEOF
- }
- return "", "", e
- }
- }
- b.UnreadByte()
-
- // Read the rest of the line and add to value.
- if line, e = readLineBytes(b); e != nil {
- return "", "", e
- }
- value += " " + string(line)
- }
- return key, value, nil
-
-Malformed:
- return "", "", ProtocolError("malformed header line: " + string(line))
-}
-
-// Internal. Parses headers in NNTP articles. Most of this is stolen from the http package,
-// and it should probably be split out into a generic RFC822 header-parsing package.
-func (c *Conn) readHeader(r *bufio.Reader) (res *Article, err os.Error) {
- res = new(Article)
- res.Header = make(map[string][]string)
- for {
- var key, value string
- if key, value, err = readKeyValue(r); err != nil {
- return nil, err
- }
- if key == "" {
- break
- }
- key = http.CanonicalHeaderKey(key)
- // RFC 3977 says nothing about duplicate keys' values being equivalent to
- // a single key joined with commas, so we keep all values seperate.
- oldvalue, present := res.Header[key]
- if present {
- sv := vector.StringVector(oldvalue)
- sv.Push(value)
- res.Header[key] = []string(sv)
- } else {
- res.Header[key] = []string{value}
- }
- }
- return res, nil
-}
diff --git a/src/pkg/nntp/nntp_test.go b/src/pkg/nntp/nntp_test.go
deleted file mode 100644
index 0944efff34..0000000000
--- a/src/pkg/nntp/nntp_test.go
+++ /dev/null
@@ -1,240 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package nntp
-
-import (
- "bufio"
- "bytes"
- "fmt"
- "io"
- "io/ioutil"
- "os"
- "strings"
- "testing"
- "time"
-)
-
-func TestSanityChecks(t *testing.T) {
- if _, err := Dial("", ""); err == nil {
- t.Fatal("Dial should require at least a destination address.")
- }
-}
-
-type faker struct {
- io.Writer
-}
-
-func (f faker) Close() os.Error {
- return nil
-}
-
-func TestBasic(t *testing.T) {
- basicServer = strings.Join(strings.Split(basicServer, "\n", -1), "\r\n")
- basicClient = strings.Join(strings.Split(basicClient, "\n", -1), "\r\n")
-
- var cmdbuf bytes.Buffer
- var fake faker
- fake.Writer = &cmdbuf
-
- conn := &Conn{conn: fake, r: bufio.NewReader(strings.NewReader(basicServer))}
-
- // Test some global commands that don't take arguments
- if _, err := conn.Capabilities(); err != nil {
- t.Fatal("should be able to request CAPABILITIES after connecting: " + err.String())
- }
-
- _, err := conn.Date()
- if err != nil {
- t.Fatal("should be able to send DATE: " + err.String())
- }
-
- /*
- Test broken until time.Parse adds this format.
- cdate := time.UTC()
- if sdate.Year != cdate.Year || sdate.Month != cdate.Month || sdate.Day != cdate.Day {
- t.Fatal("DATE seems off, probably erroneous: " + sdate.String())
- }
- */
-
- // Test LIST (implicit ACTIVE)
- if _, err = conn.List(); err != nil {
- t.Fatal("LIST should work: " + err.String())
- }
-
- tt := new(time.Time)
- tt.Year = 2010
- tt.Month = 3
- tt.Day = 1
-
- const grp = "gmane.comp.lang.go.general"
- _, l, h, err := conn.Group(grp)
- if err != nil {
- t.Fatal("Group shouldn't error: " + err.String())
- }
-
- // test STAT, NEXT, and LAST
- if _, _, err = conn.Stat(""); err != nil {
- t.Fatal("should be able to STAT after selecting a group: " + err.String())
- }
- if _, _, err = conn.Next(); err != nil {
- t.Fatal("should be able to NEXT after selecting a group: " + err.String())
- }
- if _, _, err = conn.Last(); err != nil {
- t.Fatal("should be able to LAST after a NEXT selecting a group: " + err.String())
- }
-
- // Can we grab articles?
- a, err := conn.Article(fmt.Sprintf("%d", l))
- if err != nil {
- t.Fatal("should be able to fetch the low article: " + err.String())
- }
- body, err := ioutil.ReadAll(a.Body)
- if err != nil {
- t.Fatal("error reading reader: " + err.String())
- }
-
- // Test that the article body doesn't get mangled.
- expectedbody := `Blah, blah.
-.A single leading .
-Fin.
-`
- if !bytes.Equal([]byte(expectedbody), body) {
- t.Fatalf("article body read incorrectly; got:\n%s\nExpected:\n%s", body, expectedbody)
- }
-
- // Test articleReader
- expectedart := `Message-Id: <b@c.d>
-
-Body.
-`
- a, err = conn.Article(fmt.Sprintf("%d", l+1))
- if err != nil {
- t.Fatal("shouldn't error reading article low+1: " + err.String())
- }
- var abuf bytes.Buffer
- _, err = a.WriteTo(&abuf)
- if err != nil {
- t.Fatal("shouldn't error writing out article: " + err.String())
- }
- actualart := abuf.String()
- if actualart != expectedart {
- t.Fatalf("articleReader broke; got:\n%s\nExpected\n%s", actualart, expectedart)
- }
-
- // Just headers?
- if _, err = conn.Head(fmt.Sprintf("%d", h)); err != nil {
- t.Fatal("should be able to fetch the high article: " + err.String())
- }
-
- // Without an id?
- if _, err = conn.Head(""); err != nil {
- t.Fatal("should be able to fetch the selected article without specifying an id: " + err.String())
- }
-
- // How about bad articles? Do they error?
- if _, err = conn.Head(fmt.Sprintf("%d", l-1)); err == nil {
- t.Fatal("shouldn't be able to fetch articles lower than low")
- }
- if _, err = conn.Head(fmt.Sprintf("%d", h+1)); err == nil {
- t.Fatal("shouldn't be able to fetch articles higher than high")
- }
-
- // Just the body?
- r, err := conn.Body(fmt.Sprintf("%d", l))
- if err != nil {
- t.Fatal("should be able to fetch the low article body" + err.String())
- }
- if _, err = ioutil.ReadAll(r); err != nil {
- t.Fatal("error reading reader: " + err.String())
- }
-
- if _, err = conn.NewNews(grp, tt); err != nil {
- t.Fatal("newnews should work: " + err.String())
- }
-
- // NewGroups
- if _, err = conn.NewGroups(tt); err != nil {
- t.Fatal("newgroups shouldn't error " + err.String())
- }
-
- if err = conn.Quit(); err != nil {
- t.Fatal("Quit shouldn't error: " + err.String())
- }
-
- actualcmds := cmdbuf.String()
- if basicClient != actualcmds {
- t.Fatalf("Got:\n%s\nExpected\n%s", actualcmds, basicClient)
- }
-}
-
-var basicServer = `101 Capability list:
-VERSION 2
-.
-111 20100329034158
-215 Blah blah
-foo 7 3 y
-bar 000008 02 m
-.
-211 100 1 100 gmane.comp.lang.go.general
-223 1 <a@b.c> status
-223 2 <b@c.d> Article retrieved
-223 1 <a@b.c> Article retrieved
-220 1 <a@b.c> article
-Path: fake!not-for-mail
-From: Someone
-Newsgroups: gmane.comp.lang.go.general
-Subject: [go-nuts] What about base members?
-Message-ID: <a@b.c>
-
-Blah, blah.
-..A single leading .
-Fin.
-.
-220 2 <b@c.d> article
-Message-ID: <b@c.d>
-
-Body.
-.
-221 100 <c@d.e> head
-Path: fake!not-for-mail
-Message-ID: <c@d.e>
-.
-221 100 <c@d.e> head
-Path: fake!not-for-mail
-Message-ID: <c@d.e>
-.
-423 Bad article number
-423 Bad article number
-222 1 <a@b.c> body
-Blah, blah.
-..A single leading .
-Fin.
-.
-230 list of new articles by message-id follows
-<d@e.c>
-.
-231 New newsgroups follow
-.
-205 Bye!
-`
-
-var basicClient = `CAPABILITIES
-DATE
-LIST
-GROUP gmane.comp.lang.go.general
-STAT
-NEXT
-LAST
-ARTICLE 1
-ARTICLE 2
-HEAD 100
-HEAD
-HEAD 0
-HEAD 101
-BODY 1
-NEWNEWS gmane.comp.lang.go.general 20100301 000000 GMT
-NEWGROUPS 20100301 000000 GMT
-QUIT
-`