func handleList(args []string, s *session, c *textproto.Conn) error { ltype := "active" if len(args) > 0 { ltype = strings.ToLower(args[0]) } if ltype == "overview.fmt" { return handleListOverviewFmt(c) } groups, err := s.backend.ListGroups(-1) if err != nil { return err } c.PrintfLine("215 list of newsgroups follows") dw := c.DotWriter() defer dw.Close() for _, g := range groups { switch ltype { case "active": fmt.Fprintf(dw, "%s %d %d %v\r\n", g.Name, g.High, g.Low, g.Posting) case "newsgroups": fmt.Fprintf(dw, "%s %s\r\n", g.Name, g.Description) } } return nil }
func collectLines(c *textproto.Conn) ([]string, error) { var ex error var line string var msg []string var ended bool line, ex = c.ReadLine() if ex != nil { return nil, ex } if !validLine(line) { return nil, ErrShortResponse } msg = append(msg, line) ended = line[3] != '-' for ex == nil && !ended { line, ex = c.ReadLine() if ex != nil { return nil, ex } msg = append(msg, line) ended = line[3] != '-' } return msg, nil }
func GetRequest(request Request, client *textproto.Conn) error { var err error defer func() { if err != nil { if err == io.EOF { } else { log.Println("Conn died with err %s", err) } } }() requestHeaders, err := client.ReadMIMEHeader() if err != nil { return err } response := fmt.Sprintf("Hello from Baboon!\nHave I seen you before?"+ "\nI know a bit about you.\n"+ "For example, your User Agent is %s", requestHeaders["User-Agent"]) serverHeaders := map[string]string{} serverHeaders["Server"] = "Baboon/0.001" serverHeaders["Content-Type"] = "text/plain" serverHeaders["Content-Length"] = strconv.Itoa(len(response)) client.PrintfLine("HTTP/1.1 200 OK") for key, value := range serverHeaders { client.PrintfLine("%s: %s", key, value) } client.PrintfLine("") client.PrintfLine(response) return nil }
func processConnData(conn *textproto.Conn) response { res := response{Data: make([]string, 0)} for { line, err := conn.ReadLine() if err != nil { res.Err = err break } if line == "OK" { break } match := mpdErrorRegexp.FindStringSubmatch(line) if match != nil { ack, err := strconv.ParseUint(match[1], 0, 0) if err != nil { res.Err = err break } cln, err := strconv.ParseUint(match[2], 0, 0) if err != nil { res.Err = err break } res.MPDErr = &MPDError{uint(ack), uint(cln), match[3], match[4]} break } res.Data = append(res.Data, line) } return res }
func (self *nntpConnection) startReader(daemon *NNTPDaemon, conn *textproto.Conn) { log.Println(self.name, "run reader mode") for { var err error select { case chnl := <-self.die: // we were asked to die // send quit conn.PrintfLine("QUIT") chnl <- true break case msgid := <-self.article: // next article to ask for log.Println(self.name, "obtaining", msgid) self.messageSetPendingState(msgid, "article") err = self.requestArticle(daemon, conn, msgid) self.messageSetProcessed(msgid) if err != nil { log.Println(self.name, "error while in reader mode:", err) break } } } // close connection conn.Close() }
func handleMode(args []string, s *session, c *textproto.Conn) error { if s.backend.AllowPost() { c.PrintfLine("200 Posting allowed") } else { c.PrintfLine("201 Posting prohibited") } return nil }
func handle(c *textproto.Conn, verifyOnly bool) { defer c.Close() defer func() { if r := recover(); r != nil { log.Println("Something went wrong:", r) } }() converse(c, verifyOnly) }
func converse(c *textproto.Conn, verifyOnly bool) { write(c, "220 At your service") var msg Msg for { s, err := read(c) if err == io.EOF { return } if verifyOnly { log.Println("Incoming:", s) } cmd := strings.ToUpper(s[:4]) switch cmd { case "EHLO": write(c, "250-8BITMIME") fallthrough case "HELO": write(c, "250 I need orders") case "MAIL": msg.From = addrRegex.FindStringSubmatch(s)[1] write(c, "250 In your name") case "RCPT": addr := addrRegex.FindStringSubmatch(s)[1] msg.To = append(msg.To, addr) write(c, "250 Defending your honour") case "DATA": if verifyOnly { write(c, "502 Verification service only") return } write(c, "354 Give me a quest!") data, err := c.ReadDotBytes() if err != nil { panic(err) } msg.Data = data defaultHandle(&msg) write(c, "250 We move") case "RSET": write(c, "250 OK") case "QUIT": write(c, "221 For the king") default: log.Println("Unknown command:", s) } } }
func handleBody(args []string, s *session, c *textproto.Conn) error { article, err := s.getArticle(args) if err != nil { return err } c.PrintfLine("222 1 %s", article.MessageId()) dw := c.DotWriter() defer dw.Close() _, err = io.Copy(dw, article.Body) return err }
func newClient(conn *textproto.Conn) (*Conn, error) { _, msg, err := conn.ReadCodeLine(200) if err != nil { return nil, err } return &Conn{ conn: conn, Banner: msg, }, nil }
func (self *nntpConnection) startReader(daemon NNTPDaemon, conn *textproto.Conn) { log.Println(self.name, "run reader mode") var err error for err == nil { // next article to ask for msgid := <-self.article err = self.requestArticle(daemon, conn, msgid) } // report error and close connection log.Println(self.name, "error while in reader mode:", err) conn.Close() }
func read(c *textproto.Conn) (string, error) { s, err := c.ReadLine() if err == io.EOF { return s, err } if err != nil { panic(err) } return s, err }
func handleHead(args []string, s *session, c *textproto.Conn) error { article, err := s.getArticle(args) if err != nil { return err } c.PrintfLine("221 1 %s", article.MessageId()) dw := c.DotWriter() defer dw.Close() for k, v := range article.Header { fmt.Fprintf(dw, "%s: %s\r\n", k, v[0]) } return nil }
func handleIHave(args []string, s *session, c *textproto.Conn) error { if !s.backend.AllowPost() { return NotWanted } // XXX: See if we have it. article, err := s.backend.GetArticle(nil, args[0]) if article != nil { return NotWanted } c.PrintfLine("335 send it") article = new(nntp.Article) article.Header, err = c.ReadMIMEHeader() if err != nil { return PostingFailed } article.Body = c.DotReader() err = s.backend.Post(article) if err != nil { return err } c.PrintfLine("235 article received OK") return nil }
func handleListOverviewFmt(dw io.Writer, c *textproto.Conn) error { err := c.PrintfLine("215 list of newsgroups follows") if err != nil { return err } _, err = fmt.Fprintln(dw, `Subject: From: Date: Message-ID: References: :bytes :lines`) return err }
func handleAuthInfo(args []string, s *session, c *textproto.Conn) error { if len(args) < 2 { return SyntaxError } if strings.ToLower(args[0]) != "user" { return SyntaxError } if s.backend.Authorized() { return c.PrintfLine("250 authenticated") } c.PrintfLine("350 Continue") a, err := c.ReadLine() parts := strings.SplitN(a, " ", 3) if strings.ToLower(parts[0]) != "authinfo" || strings.ToLower(parts[1]) != "pass" { return SyntaxError } b, err := s.backend.Authenticate(args[1], parts[2]) if err == nil { c.PrintfLine("250 authenticated") if b != nil { s.backend = b } } return err }
func handleCap(args []string, s *session, c *textproto.Conn) error { c.PrintfLine("101 Capability list:") dw := c.DotWriter() defer dw.Close() fmt.Fprintf(dw, "VERSION 2\n") fmt.Fprintf(dw, "READER\n") if s.backend.AllowPost() { fmt.Fprintf(dw, "POST\n") fmt.Fprintf(dw, "IHAVE\n") } fmt.Fprintf(dw, "OVER\n") fmt.Fprintf(dw, "XOVER\n") fmt.Fprintf(dw, "LIST ACTIVE NEWSGROUPS OVERVIEW.FMT\n") return nil }
func handleListOverviewFmt(c *textproto.Conn) error { err := c.PrintfLine("215 Order of fields in overview database.") if err != nil { return err } dw := c.DotWriter() defer dw.Close() _, err = fmt.Fprintln(dw, `Subject: From: Date: Message-ID: References: :bytes :lines`) return err }
func handleGroup(args []string, s *session, c *textproto.Conn) error { if len(args) < 1 { return NoSuchGroup } group, err := s.backend.GetGroup(args[0]) if err != nil { return err } s.group = group c.PrintfLine("211 %d %d %d %s", group.Count, group.Low, group.High, group.Name) return nil }
// ask for an article from the remote server // feed it to the daemon if we get it func (self *nntpConnection) requestArticle(daemon NNTPDaemon, conn *textproto.Conn, msgid string) (err error) { log.Println(self.name, "asking for", msgid) // send command err = conn.PrintfLine("ARTICLE %s", msgid) // read response code, line, err := conn.ReadCodeLine(-1) if code == 220 { // awwww yeh we got it var hdr textproto.MIMEHeader // read header hdr, err = conn.ReadMIMEHeader() if err == nil { // prepare to read body dr := conn.DotReader() // check header and decide if we want this reason, err := self.checkMIMEHeader(daemon, hdr) if err == nil { if len(reason) > 0 { log.Println(self.name, "discarding", msgid, reason) // we don't want it, discard io.Copy(ioutil.Discard, dr) daemon.database.BanArticle(msgid, reason) } else { // yeh we want it open up a file to store it in f := daemon.store.CreateTempFile(msgid) if f == nil { // already being loaded elsewhere } else { // write header to file writeMIMEHeader(f, hdr) // write article body to file _, _ = io.Copy(f, dr) // close file f.Close() log.Println(msgid, "obtained via reader from", self.name) // tell daemon to load article via infeed daemon.infeed_load <- msgid } } } else { // error happened while processing log.Println(self.name, "error happend while processing MIME header", err) } } else { // error happened while reading header log.Println(self.name, "error happened while reading MIME header", err) } } else if code == 430 { // they don't know it D: log.Println(msgid, "not known by", self.name) } else { // invalid response log.Println(self.name, "invald response to ARTICLE:", code, line) } return }
// switch modes func (self *nntpConnection) modeSwitch(mode string, conn *textproto.Conn) (success bool, err error) { self.access.Lock() mode = strings.ToUpper(mode) conn.PrintfLine("MODE %s", mode) var code int code, _, err = conn.ReadCodeLine(-1) if code >= 200 && code < 300 { // accepted mode change if len(self.mode) > 0 { log.Printf(self.name, "mode switch %s -> %s", self.mode, mode) } else { log.Println(self.name, "switched to mode", mode) } self.mode = mode success = len(self.mode) > 0 } self.access.Unlock() return }
func NewCommandRequest(c *textproto.Conn) (*CommandRequest, error) { verb, err := c.ReadLine() if err != nil { return nil, err } headers, err := c.ReadMIMEHeader() if err != nil { return nil, err } // read body here return &CommandRequest{ Method: verb, Headers: headers, //Body: body, }, nil }
// handle streaming events // this function should send only func (self *nntpConnection) handleStreaming(daemon *NNTPDaemon, conn *textproto.Conn) (err error) { for err == nil { select { case chnl := <-self.die: // someone asked us to die conn.PrintfLine("QUIT") conn.Close() chnl <- true return case msgid := <-self.check: err = self.handleStreamEvent(nntpCHECK(msgid), daemon, conn) self.messageSetPendingState(msgid, "check") case msgid := <-self.takethis: self.messageSetPendingState(msgid, "takethis") err = self.handleStreamEvent(nntpTAKETHIS(msgid), daemon, conn) } } return }
func cmd(c *textproto.Conn, expectedCode int, format string, args ...interface{}) error { id, err := c.Cmd(format, args...) if err != nil { return err } c.StartResponse(id) _, _, err = c.ReadResponse(expectedCode) c.EndResponse(id) return err }
func CloseConn(conn *textproto.Conn) error { id, err := conn.Cmd("close") if err != nil { return err } conn.StartResponse(id) conn.EndResponse(id) err = conn.Close() if err != nil { return err } return nil }
func (s *server) readRequest(p *textproto.Conn) (*request, error) { line, err := p.ReadLine() if err == io.EOF { return nil, err } if err != nil { log.Printf("reading request failed: %v\n", err) return nil, err } args := parseArgs(line) if len(args) == 0 { return &request{typ: simple, args: args}, nil } switch args[0] { case "command_list_ok_begin": var cmdList [][]string for { line, err := p.ReadLine() if err == io.EOF { return nil, err } if err != nil { log.Printf("reading request failed: %v\n", err) return nil, err } args = parseArgs(line) if len(args) > 0 && args[0] == "command_list_end" { break } cmdList = append(cmdList, args) } return &request{typ: commandListOk, cmdList: cmdList}, nil case "idle": return &request{typ: idle, args: args}, nil case "noidle": return &request{typ: noIdle, args: args}, nil } return &request{typ: simple, args: args}, nil }
func (s *server) writeIdleResponse(p *textproto.Conn, id uint, quit chan bool, subsystems []string) { p.StartResponse(id) defer p.EndResponse(id) req := &idleRequest{ endTokenc: make(chan uint), eventc: make(chan string, 1), subsystems: subsystems, } s.idleStartc <- req token := <-req.endTokenc select { case name := <-req.eventc: p.PrintfLine("changed: %s", name) p.PrintfLine("OK") <-quit case <-quit: p.PrintfLine("OK") } s.idleEndc <- token }
// handle sending 1 stream event func (self *nntpConnection) handleStreamEvent(ev nntpStreamEvent, daemon *NNTPDaemon, conn *textproto.Conn) (err error) { if ValidMessageID(ev.MessageID()) { cmd, msgid := ev.Command(), ev.MessageID() if cmd == "TAKETHIS" { // open message for reading var rc io.ReadCloser rc, err = daemon.store.OpenMessage(msgid) if err == nil { err = conn.PrintfLine("%s", ev) // time to send dw := conn.DotWriter() _, err = io.Copy(dw, rc) err = dw.Close() rc.Close() self.messageSetPendingState(msgid, "sent") } else { log.Println(self.name, "didn't send", msgid, err) self.messageSetProcessed(msgid) // ignore this error err = nil } } else if cmd == "CHECK" { conn.PrintfLine("%s", ev) } else { log.Println("invalid stream command", ev) } } return }
// handle streaming event // this function should send only func (self *nntpConnection) handleStreaming(daemon NNTPDaemon, reader bool, conn *textproto.Conn) (err error) { for err == nil { ev := <-self.stream log.Println(self.name, ev) if ValidMessageID(ev.MessageID()) { cmd, msgid := ev.Command(), ev.MessageID() if cmd == "TAKETHIS" { fname := daemon.store.GetFilename(msgid) if CheckFile(fname) { f, err := os.Open(fname) if err == nil { err = conn.PrintfLine("%s", ev) // time to send dw := conn.DotWriter() _, err = io.Copy(dw, f) err = dw.Close() f.Close() } } else { log.Println(self.name, "didn't send", msgid, "we don't have it locally") } } else if cmd == "CHECK" { conn.PrintfLine("%s", ev) } else { log.Println("invalid stream command", ev) } } } return }
// handle streaming event // this function should send only func (self nntpConnection) handleStreaming(daemon NNTPDaemon, reader bool, conn *textproto.Conn) (err error) { select { case msgid := <-self.check: log.Println(self.name, "CHECK", msgid) err = conn.PrintfLine("CHECK %s", msgid) case msgid := <-self.take: // send a file via TAKETHIS if ValidMessageID(msgid) { fname := daemon.store.GetFilename(msgid) if CheckFile(fname) { f, err := os.Open(fname) if err == nil { // time to send err = conn.PrintfLine("TAKETHIS %s", msgid) dw := conn.DotWriter() _, err = io.Copy(dw, f) err = dw.Close() f.Close() } } else { log.Println(self.name, "didn't send", msgid, "we don't have it locally") } } } return }