Blame


1 807f644f 2024-03-28 o package nntp
2 807f644f 2024-03-28 o
3 807f644f 2024-03-28 o import (
4 807f644f 2024-03-28 o "fmt"
5 807f644f 2024-03-28 o "io"
6 807f644f 2024-03-28 o "log"
7 807f644f 2024-03-28 o "math"
8 807f644f 2024-03-28 o "net"
9 807f644f 2024-03-28 o "net/textproto"
10 807f644f 2024-03-28 o "strconv"
11 807f644f 2024-03-28 o "strings"
12 807f644f 2024-03-28 o )
13 807f644f 2024-03-28 o
14 807f644f 2024-03-28 o // Handler is a low-level protocol handler
15 807f644f 2024-03-28 o type Handler func(args []string, s *session, c *textproto.Conn) error
16 807f644f 2024-03-28 o
17 807f644f 2024-03-28 o // A NumberedArticle provides local sequence nubers to articles When
18 807f644f 2024-03-28 o // listing articles in a group.
19 807f644f 2024-03-28 o type NumberedArticle struct {
20 807f644f 2024-03-28 o Num int64
21 807f644f 2024-03-28 o Article *Article
22 807f644f 2024-03-28 o }
23 807f644f 2024-03-28 o
24 807f644f 2024-03-28 o // The Backend that provides the things and does the stuff.
25 807f644f 2024-03-28 o type Backend interface {
26 807f644f 2024-03-28 o ListGroups(max int) ([]*Group, error)
27 807f644f 2024-03-28 o GetGroup(name string) (*Group, error)
28 807f644f 2024-03-28 o GetArticle(g *Group, id string) (*Article, error)
29 807f644f 2024-03-28 o GetArticles(g *Group, from, to int64) ([]NumberedArticle, error)
30 807f644f 2024-03-28 o Authorized() bool
31 807f644f 2024-03-28 o // Authenticate and optionally swap out the backend for this session.
32 807f644f 2024-03-28 o // You may return nil to continue using the same backend.
33 807f644f 2024-03-28 o Authenticate(user, pass string) (Backend, error)
34 807f644f 2024-03-28 o AllowPost() bool
35 807f644f 2024-03-28 o Post(article *Article) error
36 807f644f 2024-03-28 o }
37 807f644f 2024-03-28 o
38 807f644f 2024-03-28 o type session struct {
39 807f644f 2024-03-28 o server *Server
40 807f644f 2024-03-28 o backend Backend
41 807f644f 2024-03-28 o group *Group
42 807f644f 2024-03-28 o }
43 807f644f 2024-03-28 o
44 807f644f 2024-03-28 o // The Server handle.
45 807f644f 2024-03-28 o type Server struct {
46 807f644f 2024-03-28 o // Handlers are dispatched by command name.
47 807f644f 2024-03-28 o Handlers map[string]Handler
48 807f644f 2024-03-28 o // The backend (your code) that provides data
49 807f644f 2024-03-28 o Backend Backend
50 807f644f 2024-03-28 o // The currently selected group.
51 807f644f 2024-03-28 o group *Group
52 807f644f 2024-03-28 o }
53 807f644f 2024-03-28 o
54 807f644f 2024-03-28 o // NewServer builds a new server handle request to a backend.
55 807f644f 2024-03-28 o func NewServer(backend Backend) *Server {
56 807f644f 2024-03-28 o rv := Server{
57 807f644f 2024-03-28 o Handlers: make(map[string]Handler),
58 807f644f 2024-03-28 o Backend: backend,
59 807f644f 2024-03-28 o }
60 807f644f 2024-03-28 o rv.Handlers[""] = handleDefault
61 807f644f 2024-03-28 o rv.Handlers["quit"] = handleQuit
62 807f644f 2024-03-28 o rv.Handlers["group"] = handleGroup
63 807f644f 2024-03-28 o rv.Handlers["list"] = handleList
64 807f644f 2024-03-28 o rv.Handlers["head"] = handleHead
65 807f644f 2024-03-28 o rv.Handlers["body"] = handleBody
66 807f644f 2024-03-28 o rv.Handlers["article"] = handleArticle
67 807f644f 2024-03-28 o rv.Handlers["post"] = handlePost
68 807f644f 2024-03-28 o rv.Handlers["ihave"] = handleIHave
69 807f644f 2024-03-28 o rv.Handlers["capabilities"] = handleCap
70 807f644f 2024-03-28 o rv.Handlers["mode"] = handleMode
71 807f644f 2024-03-28 o rv.Handlers["authinfo"] = handleAuthInfo
72 807f644f 2024-03-28 o rv.Handlers["newgroups"] = handleNewGroups
73 807f644f 2024-03-28 o rv.Handlers["over"] = handleOver
74 807f644f 2024-03-28 o rv.Handlers["xover"] = handleOver
75 807f644f 2024-03-28 o return &rv
76 807f644f 2024-03-28 o }
77 807f644f 2024-03-28 o
78 807f644f 2024-03-28 o func (s *session) dispatchCommand(cmd string, args []string,
79 807f644f 2024-03-28 o c *textproto.Conn) (err error) {
80 807f644f 2024-03-28 o
81 807f644f 2024-03-28 o handler, found := s.server.Handlers[strings.ToLower(cmd)]
82 807f644f 2024-03-28 o if !found {
83 807f644f 2024-03-28 o handler, found = s.server.Handlers[""]
84 807f644f 2024-03-28 o if !found {
85 807f644f 2024-03-28 o panic("No default handler.")
86 807f644f 2024-03-28 o }
87 807f644f 2024-03-28 o }
88 807f644f 2024-03-28 o return handler(args, s, c)
89 807f644f 2024-03-28 o }
90 807f644f 2024-03-28 o
91 807f644f 2024-03-28 o // Process an NNTP session.
92 807f644f 2024-03-28 o func (s *Server) Process(nc net.Conn) {
93 807f644f 2024-03-28 o defer nc.Close()
94 807f644f 2024-03-28 o c := textproto.NewConn(nc)
95 807f644f 2024-03-28 o
96 807f644f 2024-03-28 o sess := &session{
97 807f644f 2024-03-28 o server: s,
98 807f644f 2024-03-28 o backend: s.Backend,
99 807f644f 2024-03-28 o group: nil,
100 807f644f 2024-03-28 o }
101 807f644f 2024-03-28 o
102 807f644f 2024-03-28 o c.PrintfLine("200 Hello!")
103 807f644f 2024-03-28 o for {
104 807f644f 2024-03-28 o l, err := c.ReadLine()
105 807f644f 2024-03-28 o if err != nil {
106 807f644f 2024-03-28 o log.Printf("Error reading from client, dropping conn: %v", err)
107 807f644f 2024-03-28 o return
108 807f644f 2024-03-28 o }
109 807f644f 2024-03-28 o cmd := strings.Split(l, " ")
110 807f644f 2024-03-28 o log.Printf("Got cmd: %+v", cmd)
111 807f644f 2024-03-28 o args := []string{}
112 807f644f 2024-03-28 o if len(cmd) > 1 {
113 807f644f 2024-03-28 o args = cmd[1:]
114 807f644f 2024-03-28 o }
115 807f644f 2024-03-28 o err = sess.dispatchCommand(cmd[0], args, c)
116 807f644f 2024-03-28 o if err != nil {
117 807f644f 2024-03-28 o _, ok := err.(*Error)
118 807f644f 2024-03-28 o switch {
119 807f644f 2024-03-28 o case err == io.EOF:
120 807f644f 2024-03-28 o // Drop this connection silently. They hung up
121 807f644f 2024-03-28 o return
122 807f644f 2024-03-28 o case ok:
123 807f644f 2024-03-28 o c.PrintfLine(err.Error())
124 807f644f 2024-03-28 o default:
125 807f644f 2024-03-28 o log.Printf("Error dispatching command, dropping conn: %v", err)
126 807f644f 2024-03-28 o return
127 807f644f 2024-03-28 o }
128 807f644f 2024-03-28 o }
129 807f644f 2024-03-28 o }
130 807f644f 2024-03-28 o }
131 807f644f 2024-03-28 o
132 807f644f 2024-03-28 o func parseRange(spec string) (low, high int64) {
133 807f644f 2024-03-28 o if spec == "" {
134 807f644f 2024-03-28 o return 0, math.MaxInt64
135 807f644f 2024-03-28 o }
136 807f644f 2024-03-28 o parts := strings.Split(spec, "-")
137 807f644f 2024-03-28 o if len(parts) == 1 {
138 807f644f 2024-03-28 o h, err := strconv.ParseInt(parts[0], 10, 64)
139 807f644f 2024-03-28 o if err != nil {
140 807f644f 2024-03-28 o h = math.MaxInt64
141 807f644f 2024-03-28 o }
142 807f644f 2024-03-28 o return 0, h
143 807f644f 2024-03-28 o }
144 807f644f 2024-03-28 o l, _ := strconv.ParseInt(parts[0], 10, 64)
145 807f644f 2024-03-28 o h, err := strconv.ParseInt(parts[1], 10, 64)
146 807f644f 2024-03-28 o if err != nil {
147 807f644f 2024-03-28 o h = math.MaxInt64
148 807f644f 2024-03-28 o }
149 807f644f 2024-03-28 o return l, h
150 807f644f 2024-03-28 o }
151 807f644f 2024-03-28 o
152 807f644f 2024-03-28 o /*
153 807f644f 2024-03-28 o "0" or article number (see below)
154 807f644f 2024-03-28 o Subject header content
155 807f644f 2024-03-28 o From header content
156 807f644f 2024-03-28 o Date header content
157 807f644f 2024-03-28 o Message-ID header content
158 807f644f 2024-03-28 o References header content
159 807f644f 2024-03-28 o :bytes metadata item
160 807f644f 2024-03-28 o :lines metadata item
161 807f644f 2024-03-28 o */
162 807f644f 2024-03-28 o
163 807f644f 2024-03-28 o func handleOver(args []string, s *session, c *textproto.Conn) error {
164 807f644f 2024-03-28 o if s.group == nil {
165 807f644f 2024-03-28 o return ErrNoGroupSelected
166 807f644f 2024-03-28 o }
167 807f644f 2024-03-28 o from, to := parseRange(args[0])
168 807f644f 2024-03-28 o articles, err := s.backend.GetArticles(s.group, from, to)
169 807f644f 2024-03-28 o if err != nil {
170 807f644f 2024-03-28 o return err
171 807f644f 2024-03-28 o }
172 807f644f 2024-03-28 o c.PrintfLine("224 here it comes")
173 807f644f 2024-03-28 o dw := c.DotWriter()
174 807f644f 2024-03-28 o defer dw.Close()
175 807f644f 2024-03-28 o for _, a := range articles {
176 807f644f 2024-03-28 o fmt.Fprintf(dw, "%d\t%s\t%s\t%s\t%s\t%s\t%d\t%d\n", a.Num,
177 807f644f 2024-03-28 o a.Article.Header.Get("Subject"),
178 807f644f 2024-03-28 o a.Article.Header.Get("From"),
179 807f644f 2024-03-28 o a.Article.Header.Get("Date"),
180 807f644f 2024-03-28 o a.Article.Header.Get("Message-Id"),
181 807f644f 2024-03-28 o a.Article.Header.Get("References"),
182 807f644f 2024-03-28 o a.Article.Bytes, a.Article.Lines)
183 807f644f 2024-03-28 o }
184 807f644f 2024-03-28 o return nil
185 807f644f 2024-03-28 o }
186 807f644f 2024-03-28 o
187 807f644f 2024-03-28 o func handleListOverviewFmt(c *textproto.Conn) error {
188 807f644f 2024-03-28 o err := c.PrintfLine("215 Order of fields in overview database.")
189 807f644f 2024-03-28 o if err != nil {
190 807f644f 2024-03-28 o return err
191 807f644f 2024-03-28 o }
192 807f644f 2024-03-28 o dw := c.DotWriter()
193 807f644f 2024-03-28 o defer dw.Close()
194 807f644f 2024-03-28 o _, err = fmt.Fprintln(dw, `Subject:
195 807f644f 2024-03-28 o From:
196 807f644f 2024-03-28 o Date:
197 807f644f 2024-03-28 o Message-ID:
198 807f644f 2024-03-28 o References:
199 807f644f 2024-03-28 o :bytes
200 807f644f 2024-03-28 o :lines`)
201 807f644f 2024-03-28 o return err
202 807f644f 2024-03-28 o }
203 807f644f 2024-03-28 o
204 807f644f 2024-03-28 o func handleList(args []string, s *session, c *textproto.Conn) error {
205 807f644f 2024-03-28 o ltype := "active"
206 807f644f 2024-03-28 o if len(args) > 0 {
207 807f644f 2024-03-28 o ltype = strings.ToLower(args[0])
208 807f644f 2024-03-28 o }
209 807f644f 2024-03-28 o
210 807f644f 2024-03-28 o if ltype == "overview.fmt" {
211 807f644f 2024-03-28 o return handleListOverviewFmt(c)
212 807f644f 2024-03-28 o }
213 807f644f 2024-03-28 o
214 807f644f 2024-03-28 o groups, err := s.backend.ListGroups(-1)
215 807f644f 2024-03-28 o if err != nil {
216 807f644f 2024-03-28 o return err
217 807f644f 2024-03-28 o }
218 807f644f 2024-03-28 o c.PrintfLine("215 list of newsgroups follows")
219 807f644f 2024-03-28 o dw := c.DotWriter()
220 807f644f 2024-03-28 o defer dw.Close()
221 807f644f 2024-03-28 o for _, g := range groups {
222 807f644f 2024-03-28 o switch ltype {
223 807f644f 2024-03-28 o case "active":
224 807f644f 2024-03-28 o fmt.Fprintf(dw, "%s %d %d %v\r\n",
225 807f644f 2024-03-28 o g.Name, g.High, g.Low, g.Posting)
226 807f644f 2024-03-28 o case "newsgroups":
227 807f644f 2024-03-28 o fmt.Fprintf(dw, "%s %s\r\n", g.Name, g.Description)
228 807f644f 2024-03-28 o }
229 807f644f 2024-03-28 o }
230 807f644f 2024-03-28 o
231 807f644f 2024-03-28 o return nil
232 807f644f 2024-03-28 o }
233 807f644f 2024-03-28 o
234 807f644f 2024-03-28 o func handleNewGroups(args []string, s *session, c *textproto.Conn) error {
235 807f644f 2024-03-28 o c.PrintfLine("231 list of newsgroups follows")
236 807f644f 2024-03-28 o c.PrintfLine(".")
237 807f644f 2024-03-28 o return nil
238 807f644f 2024-03-28 o }
239 807f644f 2024-03-28 o
240 807f644f 2024-03-28 o func handleDefault(args []string, s *session, c *textproto.Conn) error {
241 807f644f 2024-03-28 o return ErrUnknownCommand
242 807f644f 2024-03-28 o }
243 807f644f 2024-03-28 o
244 807f644f 2024-03-28 o func handleQuit(args []string, s *session, c *textproto.Conn) error {
245 807f644f 2024-03-28 o c.PrintfLine("205 bye")
246 807f644f 2024-03-28 o return io.EOF
247 807f644f 2024-03-28 o }
248 807f644f 2024-03-28 o
249 807f644f 2024-03-28 o func handleGroup(args []string, s *session, c *textproto.Conn) error {
250 807f644f 2024-03-28 o if len(args) < 1 {
251 807f644f 2024-03-28 o return ErrNoSuchGroup
252 807f644f 2024-03-28 o }
253 807f644f 2024-03-28 o
254 807f644f 2024-03-28 o group, err := s.backend.GetGroup(args[0])
255 807f644f 2024-03-28 o if err != nil {
256 807f644f 2024-03-28 o return err
257 807f644f 2024-03-28 o }
258 807f644f 2024-03-28 o
259 807f644f 2024-03-28 o s.group = group
260 807f644f 2024-03-28 o
261 807f644f 2024-03-28 o c.PrintfLine("211 %d %d %d %s",
262 807f644f 2024-03-28 o group.Count, group.Low, group.High, group.Name)
263 807f644f 2024-03-28 o return nil
264 807f644f 2024-03-28 o }
265 807f644f 2024-03-28 o
266 807f644f 2024-03-28 o func (s *session) getArticle(args []string) (*Article, error) {
267 807f644f 2024-03-28 o if s.group == nil {
268 807f644f 2024-03-28 o return nil, ErrNoGroupSelected
269 807f644f 2024-03-28 o }
270 807f644f 2024-03-28 o return s.backend.GetArticle(s.group, args[0])
271 807f644f 2024-03-28 o }
272 807f644f 2024-03-28 o
273 807f644f 2024-03-28 o /*
274 807f644f 2024-03-28 o Syntax
275 807f644f 2024-03-28 o HEAD message-id
276 807f644f 2024-03-28 o HEAD number
277 807f644f 2024-03-28 o HEAD
278 807f644f 2024-03-28 o
279 807f644f 2024-03-28 o
280 807f644f 2024-03-28 o First form (message-id specified)
281 807f644f 2024-03-28 o 221 0|n message-id Headers follow (multi-line)
282 807f644f 2024-03-28 o 430 No article with that message-id
283 807f644f 2024-03-28 o
284 807f644f 2024-03-28 o Second form (article number specified)
285 807f644f 2024-03-28 o 221 n message-id Headers follow (multi-line)
286 807f644f 2024-03-28 o 412 No newsgroup selected
287 807f644f 2024-03-28 o 423 No article with that number
288 807f644f 2024-03-28 o
289 807f644f 2024-03-28 o Third form (current article number used)
290 807f644f 2024-03-28 o 221 n message-id Headers follow (multi-line)
291 807f644f 2024-03-28 o 412 No newsgroup selected
292 807f644f 2024-03-28 o 420 Current article number is invalid
293 807f644f 2024-03-28 o */
294 807f644f 2024-03-28 o
295 807f644f 2024-03-28 o func handleHead(args []string, s *session, c *textproto.Conn) error {
296 807f644f 2024-03-28 o article, err := s.getArticle(args)
297 807f644f 2024-03-28 o if err != nil {
298 807f644f 2024-03-28 o return err
299 807f644f 2024-03-28 o }
300 807f644f 2024-03-28 o c.PrintfLine("221 1 %s", article.MessageID())
301 807f644f 2024-03-28 o dw := c.DotWriter()
302 807f644f 2024-03-28 o defer dw.Close()
303 807f644f 2024-03-28 o for k, v := range article.Header {
304 807f644f 2024-03-28 o fmt.Fprintf(dw, "%s: %s\r\n", k, v[0])
305 807f644f 2024-03-28 o }
306 807f644f 2024-03-28 o return nil
307 807f644f 2024-03-28 o }
308 807f644f 2024-03-28 o
309 807f644f 2024-03-28 o /*
310 807f644f 2024-03-28 o Syntax
311 807f644f 2024-03-28 o BODY message-id
312 807f644f 2024-03-28 o BODY number
313 807f644f 2024-03-28 o BODY
314 807f644f 2024-03-28 o
315 807f644f 2024-03-28 o Responses
316 807f644f 2024-03-28 o
317 807f644f 2024-03-28 o First form (message-id specified)
318 807f644f 2024-03-28 o 222 0|n message-id Body follows (multi-line)
319 807f644f 2024-03-28 o 430 No article with that message-id
320 807f644f 2024-03-28 o
321 807f644f 2024-03-28 o Second form (article number specified)
322 807f644f 2024-03-28 o 222 n message-id Body follows (multi-line)
323 807f644f 2024-03-28 o 412 No newsgroup selected
324 807f644f 2024-03-28 o 423 No article with that number
325 807f644f 2024-03-28 o
326 807f644f 2024-03-28 o Third form (current article number used)
327 807f644f 2024-03-28 o 222 n message-id Body follows (multi-line)
328 807f644f 2024-03-28 o 412 No newsgroup selected
329 807f644f 2024-03-28 o 420 Current article number is invalid
330 807f644f 2024-03-28 o
331 807f644f 2024-03-28 o Parameters
332 807f644f 2024-03-28 o number Requested article number
333 807f644f 2024-03-28 o n Returned article number
334 807f644f 2024-03-28 o message-id Article message-id
335 807f644f 2024-03-28 o */
336 807f644f 2024-03-28 o
337 807f644f 2024-03-28 o func handleBody(args []string, s *session, c *textproto.Conn) error {
338 807f644f 2024-03-28 o article, err := s.getArticle(args)
339 807f644f 2024-03-28 o if err != nil {
340 807f644f 2024-03-28 o return err
341 807f644f 2024-03-28 o }
342 807f644f 2024-03-28 o c.PrintfLine("222 1 %s", article.MessageID())
343 807f644f 2024-03-28 o dw := c.DotWriter()
344 807f644f 2024-03-28 o defer dw.Close()
345 807f644f 2024-03-28 o _, err = io.Copy(dw, article.Body)
346 807f644f 2024-03-28 o return err
347 807f644f 2024-03-28 o }
348 807f644f 2024-03-28 o
349 807f644f 2024-03-28 o /*
350 807f644f 2024-03-28 o Syntax
351 807f644f 2024-03-28 o ARTICLE message-id
352 807f644f 2024-03-28 o ARTICLE number
353 807f644f 2024-03-28 o ARTICLE
354 807f644f 2024-03-28 o
355 807f644f 2024-03-28 o Responses
356 807f644f 2024-03-28 o
357 807f644f 2024-03-28 o First form (message-id specified)
358 807f644f 2024-03-28 o 220 0|n message-id Article follows (multi-line)
359 807f644f 2024-03-28 o 430 No article with that message-id
360 807f644f 2024-03-28 o
361 807f644f 2024-03-28 o Second form (article number specified)
362 807f644f 2024-03-28 o 220 n message-id Article follows (multi-line)
363 807f644f 2024-03-28 o 412 No newsgroup selected
364 807f644f 2024-03-28 o 423 No article with that number
365 807f644f 2024-03-28 o
366 807f644f 2024-03-28 o Third form (current article number used)
367 807f644f 2024-03-28 o 220 n message-id Article follows (multi-line)
368 807f644f 2024-03-28 o 412 No newsgroup selected
369 807f644f 2024-03-28 o 420 Current article number is invalid
370 807f644f 2024-03-28 o
371 807f644f 2024-03-28 o Parameters
372 807f644f 2024-03-28 o number Requested article number
373 807f644f 2024-03-28 o n Returned article number
374 807f644f 2024-03-28 o message-id Article message-id
375 807f644f 2024-03-28 o */
376 807f644f 2024-03-28 o
377 807f644f 2024-03-28 o func handleArticle(args []string, s *session, c *textproto.Conn) error {
378 807f644f 2024-03-28 o article, err := s.getArticle(args)
379 807f644f 2024-03-28 o if err != nil {
380 807f644f 2024-03-28 o return err
381 807f644f 2024-03-28 o }
382 807f644f 2024-03-28 o c.PrintfLine("220 1 %s", article.MessageID())
383 807f644f 2024-03-28 o dw := c.DotWriter()
384 807f644f 2024-03-28 o defer dw.Close()
385 807f644f 2024-03-28 o
386 807f644f 2024-03-28 o for k, v := range article.Header {
387 807f644f 2024-03-28 o fmt.Fprintf(dw, "%s: %s\r\n", k, v[0])
388 807f644f 2024-03-28 o }
389 807f644f 2024-03-28 o
390 807f644f 2024-03-28 o fmt.Fprintln(dw, "")
391 807f644f 2024-03-28 o
392 807f644f 2024-03-28 o _, err = io.Copy(dw, article.Body)
393 807f644f 2024-03-28 o return err
394 807f644f 2024-03-28 o }
395 807f644f 2024-03-28 o
396 807f644f 2024-03-28 o /*
397 807f644f 2024-03-28 o Syntax
398 807f644f 2024-03-28 o POST
399 807f644f 2024-03-28 o
400 807f644f 2024-03-28 o Responses
401 807f644f 2024-03-28 o
402 807f644f 2024-03-28 o Initial responses
403 807f644f 2024-03-28 o 340 Send article to be posted
404 807f644f 2024-03-28 o 440 Posting not permitted
405 807f644f 2024-03-28 o
406 807f644f 2024-03-28 o Subsequent responses
407 807f644f 2024-03-28 o 240 Article received OK
408 807f644f 2024-03-28 o 441 Posting failed
409 807f644f 2024-03-28 o */
410 807f644f 2024-03-28 o
411 807f644f 2024-03-28 o func handlePost(args []string, s *session, c *textproto.Conn) error {
412 807f644f 2024-03-28 o if !s.backend.AllowPost() {
413 807f644f 2024-03-28 o return ErrPostingNotPermitted
414 807f644f 2024-03-28 o }
415 807f644f 2024-03-28 o
416 807f644f 2024-03-28 o c.PrintfLine("340 Go ahead")
417 807f644f 2024-03-28 o var err error
418 807f644f 2024-03-28 o var article Article
419 807f644f 2024-03-28 o article.Header, err = c.ReadMIMEHeader()
420 807f644f 2024-03-28 o if err != nil {
421 807f644f 2024-03-28 o return ErrPostingFailed
422 807f644f 2024-03-28 o }
423 807f644f 2024-03-28 o article.Body = c.DotReader()
424 807f644f 2024-03-28 o err = s.backend.Post(&article)
425 807f644f 2024-03-28 o if err != nil {
426 807f644f 2024-03-28 o return err
427 807f644f 2024-03-28 o }
428 807f644f 2024-03-28 o c.PrintfLine("240 article received OK")
429 807f644f 2024-03-28 o return nil
430 807f644f 2024-03-28 o }
431 807f644f 2024-03-28 o
432 807f644f 2024-03-28 o func handleIHave(args []string, s *session, c *textproto.Conn) error {
433 807f644f 2024-03-28 o if !s.backend.AllowPost() {
434 807f644f 2024-03-28 o return ErrNotWanted
435 807f644f 2024-03-28 o }
436 807f644f 2024-03-28 o
437 807f644f 2024-03-28 o // XXX: See if we have it.
438 807f644f 2024-03-28 o article, err := s.backend.GetArticle(nil, args[0])
439 807f644f 2024-03-28 o if article != nil {
440 807f644f 2024-03-28 o return ErrNotWanted
441 807f644f 2024-03-28 o }
442 807f644f 2024-03-28 o
443 807f644f 2024-03-28 o c.PrintfLine("335 send it")
444 807f644f 2024-03-28 o article = &Article{}
445 807f644f 2024-03-28 o article.Header, err = c.ReadMIMEHeader()
446 807f644f 2024-03-28 o if err != nil {
447 807f644f 2024-03-28 o return ErrPostingFailed
448 807f644f 2024-03-28 o }
449 807f644f 2024-03-28 o article.Body = c.DotReader()
450 807f644f 2024-03-28 o err = s.backend.Post(article)
451 807f644f 2024-03-28 o if err != nil {
452 807f644f 2024-03-28 o return err
453 807f644f 2024-03-28 o }
454 807f644f 2024-03-28 o c.PrintfLine("235 article received OK")
455 807f644f 2024-03-28 o return nil
456 807f644f 2024-03-28 o }
457 807f644f 2024-03-28 o
458 807f644f 2024-03-28 o func handleCap(args []string, s *session, c *textproto.Conn) error {
459 807f644f 2024-03-28 o c.PrintfLine("101 Capability list:")
460 807f644f 2024-03-28 o dw := c.DotWriter()
461 807f644f 2024-03-28 o defer dw.Close()
462 807f644f 2024-03-28 o
463 807f644f 2024-03-28 o fmt.Fprintf(dw, "VERSION 2\n")
464 807f644f 2024-03-28 o fmt.Fprintf(dw, "READER\n")
465 807f644f 2024-03-28 o if s.backend.AllowPost() {
466 807f644f 2024-03-28 o fmt.Fprintf(dw, "POST\n")
467 807f644f 2024-03-28 o fmt.Fprintf(dw, "IHAVE\n")
468 807f644f 2024-03-28 o }
469 807f644f 2024-03-28 o fmt.Fprintf(dw, "OVER\n")
470 807f644f 2024-03-28 o fmt.Fprintf(dw, "XOVER\n")
471 807f644f 2024-03-28 o fmt.Fprintf(dw, "LIST ACTIVE NEWSGROUPS OVERVIEW.FMT\n")
472 807f644f 2024-03-28 o return nil
473 807f644f 2024-03-28 o }
474 807f644f 2024-03-28 o
475 807f644f 2024-03-28 o func handleMode(args []string, s *session, c *textproto.Conn) error {
476 807f644f 2024-03-28 o if s.backend.AllowPost() {
477 807f644f 2024-03-28 o c.PrintfLine("200 Posting allowed")
478 807f644f 2024-03-28 o } else {
479 807f644f 2024-03-28 o c.PrintfLine("201 Posting prohibited")
480 807f644f 2024-03-28 o }
481 807f644f 2024-03-28 o return nil
482 807f644f 2024-03-28 o }
483 807f644f 2024-03-28 o
484 807f644f 2024-03-28 o func handleAuthInfo(args []string, s *session, c *textproto.Conn) error {
485 807f644f 2024-03-28 o if len(args) < 2 {
486 807f644f 2024-03-28 o return ErrSyntax
487 807f644f 2024-03-28 o }
488 807f644f 2024-03-28 o if strings.ToLower(args[0]) != "user" {
489 807f644f 2024-03-28 o return ErrSyntax
490 807f644f 2024-03-28 o }
491 807f644f 2024-03-28 o
492 807f644f 2024-03-28 o if s.backend.Authorized() {
493 807f644f 2024-03-28 o return c.PrintfLine("250 authenticated")
494 807f644f 2024-03-28 o }
495 807f644f 2024-03-28 o
496 807f644f 2024-03-28 o c.PrintfLine("350 Continue")
497 807f644f 2024-03-28 o a, err := c.ReadLine()
498 807f644f 2024-03-28 o parts := strings.SplitN(a, " ", 3)
499 807f644f 2024-03-28 o if strings.ToLower(parts[0]) != "authinfo" || strings.ToLower(parts[1]) != "pass" {
500 807f644f 2024-03-28 o return ErrSyntax
501 807f644f 2024-03-28 o }
502 807f644f 2024-03-28 o b, err := s.backend.Authenticate(args[1], parts[2])
503 807f644f 2024-03-28 o if err == nil {
504 807f644f 2024-03-28 o c.PrintfLine("250 authenticated")
505 807f644f 2024-03-28 o if b != nil {
506 807f644f 2024-03-28 o s.backend = b
507 807f644f 2024-03-28 o }
508 807f644f 2024-03-28 o }
509 807f644f 2024-03-28 o return err
510 807f644f 2024-03-28 o }