1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
|
diff --git a/authinfo.go b/authinfo.go
index 79551e1a4606ebc9803a40dca81e9287b42ed54e..f9797a9a1a3b497911958f8d6a538fc80518ff2e 100644
--- a/authinfo.go
+++ b/authinfo.go
@@ -6,7 +6,8 @@ "errors"
"fmt"
)
-// Login implements RFC4643 authentication
+// LoginUserPass implements RFC4643 authentication without SASL. This is the
+// most common way of authentication.
func (c *Conn) LoginUserPass(ctx context.Context, username, pass string) error {
if c.caps&CapAuthInfoUser == 0 {
return ErrCapabilityNotSupported
@@ -26,7 +27,10 @@ }
switch r.Status.Code {
case StatusAuthAccepted:
- c.Capabilities(ctx)
+ _, err = c.Capabilities(ctx)
+ if err != nil {
+ return fmt.Errorf("failed to update capabilities: %w", err)
+ }
return nil
case StatusAuthFailed:
return ErrAuthFailed
diff --git a/conn.go b/conn.go
index 02db7d58ff8b086608d26b5add1b618c14752eb3..cfd7d3f519b7aa743fe0d63f418588e3d307ab29 100644
--- a/conn.go
+++ b/conn.go
@@ -78,17 +78,25 @@ switch code {
case StatusServiceAvailable, StatusServiceNoPosting:
return c, nil
case StatusTemporarilyUnavailable:
+ defer c.c.Close()
return nil, ErrTemporarilyUnavailable
case StatusPermanentlyUnavailable:
+ defer c.c.Close()
return nil, ErrPermanentlyUnavailable
default:
return nil, fmt.Errorf("%w: %d", ErrUnexpectedResponse, code)
}
}
+// Close terminates all running operations and closes the connection and
+// cancels pending operations.
func (c *Conn) Close(ctx context.Context) error {
c.cancel()
defer c.c.Close()
+
+ origCtx := c.ctx
+ defer func() { c.ctx = origCtx }()
+ c.ctx = ctx
resp, err := c.CmdNoBody(ctx, "QUIT")
if err != nil {
@@ -295,7 +303,12 @@ }
}
var (
+ // ErrTemporarilyUnavailable signals that the server indicates a
+ // temporary unavailability. It corresponds to response code 400
ErrTemporarilyUnavailable = errors.New("server temporarily not available")
+ // ErrPermanentlyUnavailable signals that the server indicates that the
+ // service has become permanently unavailable. It corresponds to
+ // response code 502
ErrPermanentlyUnavailable = errors.New("server permanently not available")
ErrRequestTimedOut = errors.New("request timed out")
ErrConnectionClosed = errors.New("connection closed")
diff --git a/doc.go b/doc.go
new file mode 100644
index 0000000000000000000000000000000000000000..ddd2b0fb55f1eab70a11db04b9b45b84155a2120
--- /dev/null
+++ b/doc.go
@@ -0,0 +1,5 @@
+// Package nntp provides an interface to NNTP servers. It is purely intended as
+// a client library and is therefore missing all server features described by
+// RFC3977. Please be aware that some popular providers – like Eweka – do not
+// properly implement this standard which may lead to unexpected behaviour.
+package nntp
diff --git a/mandatory.go b/mandatory.go
index 17530f3971467541703cafd59df6d3851a285329..9ce89a96d8f3affee4c75a70339b708e54716fa9 100644
--- a/mandatory.go
+++ b/mandatory.go
@@ -48,7 +48,7 @@ }
switch resp.Status.Code {
case StatusHelpTextFollows:
- return fmt.Sprintf("%s", resp.Body), nil
+ return string(resp.Body), nil
default:
return "", ErrUnexpectedResponse
}
diff --git a/mandatory_test.go b/mandatory_test.go
index f4d5626c522d6ab4ea752a2267aa328b4ffffc48..e9c0f85bfb51be3cf1fbc94a9d81e5602b29a2cb 100644
--- a/mandatory_test.go
+++ b/mandatory_test.go
@@ -108,10 +108,6 @@
if len(header["Message-Id"]) == 0 {
t.Errorf("Message-ID: not set")
}
-
- if t.Failed() {
- // t.Logf("Headers: %#v", header)
- }
})
}
}
@@ -139,7 +135,7 @@ }
contained := make(map[string]bool)
for _, v := range strings.Split(help, "\n") {
- v = strings.TrimLeft(v, " ")
+ v = strings.TrimLeft(v, " ")
contained[strings.Split(v, " ")[0]] = true
}
|