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 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 }
// 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 }
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 }
func handlePost(args []string, s *session, c *textproto.Conn) error { if !s.backend.AllowPost() { return PostingNotPermitted } c.PrintfLine("340 Go ahead") var err error var article 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("240 article received OK") return nil }
func (self nntpConnection) handleLine(daemon NNTPDaemon, code int, line string, conn *textproto.Conn) (err error) { parts := strings.Split(line, " ") var msgid string if code == 0 && len(parts) > 1 { msgid = parts[1] } else { msgid = parts[0] } if code == 238 { if ValidMessageID(msgid) { log.Println("sending", msgid, "to", self.name) // send the article to us self.take <- msgid } } else if code == 239 { // successful TAKETHIS log.Println(msgid, "sent via", self.name) // TODO: remember success } else if code == 431 { // CHECK said we would like this article later log.Println("defer sending", msgid, "to", self.name) go self.articleDefer(msgid) } else if code == 439 { // TAKETHIS failed log.Println(msgid, "was not sent to", self.name, "denied:", line) // TODO: remember denial } else if code == 438 { // they don't want the article // TODO: remeber rejection } else { // handle command parts := strings.Split(line, " ") if len(parts) == 2 { cmd := parts[0] if cmd == "MODE" { if parts[1] == "READER" { // reader mode self.mode = "READER" log.Println(self.name, "switched to reader mode") conn.PrintfLine("201 No posting Permitted") } else if parts[1] == "STREAM" { // wut? we're already in streaming mode log.Println(self.name, "already in streaming mode") conn.PrintfLine("203 Streaming enabled brah") } else { // invalid log.Println(self.name, "got invalid mode request", parts[1]) conn.PrintfLine("501 invalid mode variant:", parts[1]) } } else if cmd == "QUIT" { // quit command conn.PrintfLine("") // close our connection and return conn.Close() return } else if cmd == "CHECK" { // handle check command msgid := parts[1] // have we seen this article? if daemon.database.HasArticle(msgid) { // yeh don't want it conn.PrintfLine("438 %s", msgid) } else if daemon.database.ArticleBanned(msgid) { // it's banned we don't want it conn.PrintfLine("438 %s", msgid) } else { // yes we do want it and we don't have it conn.PrintfLine("238 %s", msgid) } } else if cmd == "TAKETHIS" { // handle takethis command var hdr textproto.MIMEHeader var reason string // read the article header hdr, err = conn.ReadMIMEHeader() if err == nil { // check the header reason, err = self.checkMIMEHeader(daemon, hdr) dr := conn.DotReader() if len(reason) > 0 { // discard, we do not want code = 439 log.Println(self.name, "rejected", msgid, reason) _, err = io.Copy(ioutil.Discard, dr) err = daemon.database.BanArticle(msgid, reason) } else { // check if we don't have the rootpost reference := hdr.Get("References") newsgroup := hdr.Get("Newsgroups") if reference != "" && ValidMessageID(reference) && !daemon.store.HasArticle(reference) && !daemon.database.IsExpired(reference) { log.Println(self.name, "got reply to", reference, "but we don't have it") daemon.ask_for_article <- ArticleEntry{reference, newsgroup} } f := daemon.store.CreateTempFile(msgid) if f == nil { log.Println(self.name, "discarding", msgid, "we are already loading it") // discard io.Copy(ioutil.Discard, dr) } else { // write header err = writeMIMEHeader(f, hdr) // write body _, err = io.Copy(f, dr) if err == nil || err == io.EOF { f.Close() // we gud, tell daemon daemon.infeed_load <- msgid } else { log.Println(self.name, "error reading message", err) } } code = 239 reason = "gotten" } } else { log.Println(self.name, "error reading mime header:", err) code = 439 reason = "error reading mime header" } conn.PrintfLine("%d %s %s", code, msgid, reason) } else if cmd == "ARTICLE" { if ValidMessageID(msgid) { if daemon.store.HasArticle(msgid) { // we have it yeh f, err := os.Open(daemon.store.GetFilename(msgid)) if err == nil { conn.PrintfLine("220 %s", msgid) dw := conn.DotWriter() _, err = io.Copy(dw, f) dw.Close() f.Close() } else { // wtf?! conn.PrintfLine("503 idkwtf happened: %s", err.Error()) } } else { // we dont got it conn.PrintfLine("430 %s", msgid) } } else { // invalid id conn.PrintfLine("500 Syntax error") } } } } return }
func (self *nntpConnection) handleLine(daemon NNTPDaemon, code int, line string, conn *textproto.Conn) (err error) { parts := strings.Split(line, " ") var msgid string if code == 0 && len(parts) > 1 { msgid = parts[1] } else { msgid = parts[0] } if code == 238 { if ValidMessageID(msgid) { self.stream <- nntpTAKETHIS(msgid) } return } else if code == 239 { // successful TAKETHIS log.Println(msgid, "sent via", self.name) return // TODO: remember success } else if code == 431 { // CHECK said we would like this article later log.Println("defer sending", msgid, "to", self.name) go self.articleDefer(msgid) } else if code == 439 { // TAKETHIS failed log.Println(msgid, "was not sent to", self.name, "denied:", line) // TODO: remember denial } else if code == 438 { // they don't want the article // TODO: remeber rejection } else { // handle command parts := strings.Split(line, " ") if len(parts) > 1 { cmd := parts[0] if cmd == "MODE" { if parts[1] == "READER" { // reader mode self.mode = "READER" log.Println(self.name, "switched to reader mode") conn.PrintfLine("201 No posting Permitted") } else if parts[1] == "STREAM" { // wut? we're already in streaming mode log.Println(self.name, "already in streaming mode") conn.PrintfLine("203 Streaming enabled brah") } else { // invalid log.Println(self.name, "got invalid mode request", parts[1]) conn.PrintfLine("501 invalid mode variant:", parts[1]) } } else if cmd == "QUIT" { // quit command conn.PrintfLine("") // close our connection and return conn.Close() return } else if cmd == "CHECK" { // handle check command msgid := parts[1] // have we seen this article? if daemon.database.HasArticle(msgid) { // yeh don't want it conn.PrintfLine("438 %s", msgid) } else if daemon.database.ArticleBanned(msgid) { // it's banned we don't want it conn.PrintfLine("438 %s", msgid) } else { // yes we do want it and we don't have it conn.PrintfLine("238 %s", msgid) } } else if cmd == "TAKETHIS" { // handle takethis command var hdr textproto.MIMEHeader var reason string // read the article header hdr, err = conn.ReadMIMEHeader() if err == nil { // check the header reason, err = self.checkMIMEHeader(daemon, hdr) dr := conn.DotReader() if len(reason) > 0 { // discard, we do not want code = 439 log.Println(self.name, "rejected", msgid, reason) _, err = io.Copy(ioutil.Discard, dr) err = daemon.database.BanArticle(msgid, reason) } else { // check if we don't have the rootpost reference := hdr.Get("References") newsgroup := hdr.Get("Newsgroups") if reference != "" && ValidMessageID(reference) && !daemon.store.HasArticle(reference) && !daemon.database.IsExpired(reference) { log.Println(self.name, "got reply to", reference, "but we don't have it") daemon.ask_for_article <- ArticleEntry{reference, newsgroup} } f := daemon.store.CreateTempFile(msgid) if f == nil { log.Println(self.name, "discarding", msgid, "we are already loading it") // discard io.Copy(ioutil.Discard, dr) } else { // write header err = writeMIMEHeader(f, hdr) // write body _, err = io.Copy(f, dr) if err == nil || err == io.EOF { f.Close() // we gud, tell daemon daemon.infeed_load <- msgid } else { log.Println(self.name, "error reading message", err) } } code = 239 reason = "gotten" } } else { log.Println(self.name, "error reading mime header:", err) code = 439 reason = "error reading mime header" } conn.PrintfLine("%d %s %s", code, msgid, reason) } else if cmd == "ARTICLE" { if ValidMessageID(msgid) { if daemon.store.HasArticle(msgid) { // we have it yeh f, err := os.Open(daemon.store.GetFilename(msgid)) if err == nil { conn.PrintfLine("220 %s", msgid) dw := conn.DotWriter() _, err = io.Copy(dw, f) dw.Close() f.Close() } else { // wtf?! conn.PrintfLine("503 idkwtf happened: %s", err.Error()) } } else { // we dont got it conn.PrintfLine("430 %s", msgid) } } else { // invalid id conn.PrintfLine("500 Syntax error") } } else if cmd == "POST" { // handle POST command conn.PrintfLine("340 Post it nigguh; end with <CR-LF>.<CR-LF>") hdr, err := conn.ReadMIMEHeader() var success bool if err == nil { hdr["Message-ID"] = []string{genMessageID(daemon.instance_name)} reason, err := self.checkMIMEHeader(daemon, hdr) success = reason == "" && err == nil if success { dr := conn.DotReader() reference := hdr.Get("References") newsgroup := hdr.Get("Newsgroups") if reference != "" && ValidMessageID(reference) && !daemon.store.HasArticle(reference) && !daemon.database.IsExpired(reference) { log.Println(self.name, "got reply to", reference, "but we don't have it") daemon.ask_for_article <- ArticleEntry{reference, newsgroup} } f := daemon.store.CreateTempFile(msgid) if f == nil { log.Println(self.name, "discarding", msgid, "we are already loading it") // discard io.Copy(ioutil.Discard, dr) } else { // write header err = writeMIMEHeader(f, hdr) // write body _, err = io.Copy(f, dr) if err == nil || err == io.EOF { f.Close() // we gud, tell daemon daemon.infeed_load <- msgid } else { log.Println(self.name, "error reading message", err) } } } } if success && err == nil { // all gud conn.PrintfLine("240 We got it, thnkxbai") } else { // failed posting if err != nil { log.Println(self.name, "failed nntp POST", err) } conn.PrintfLine("441 Posting Failed") } } else if cmd == "IHAVE" { // handle IHAVE command msgid := parts[1] if daemon.database.HasArticleLocal(msgid) || daemon.database.HasArticle(msgid) || daemon.database.ArticleBanned(msgid) { // we don't want it conn.PrintfLine("435 Article Not Wanted") } else { // gib we want conn.PrintfLine("335 Send it plz") hdr, err := conn.ReadMIMEHeader() if err == nil { // check the header var reason string reason, err = self.checkMIMEHeader(daemon, hdr) dr := conn.DotReader() if len(reason) > 0 { // discard, we do not want log.Println(self.name, "rejected", msgid, reason) _, err = io.Copy(ioutil.Discard, dr) // ignore this _ = daemon.database.BanArticle(msgid, reason) conn.PrintfLine("437 Rejected do not send again bro") } else { // check if we don't have the rootpost reference := hdr.Get("References") newsgroup := hdr.Get("Newsgroups") if reference != "" && ValidMessageID(reference) && !daemon.store.HasArticle(reference) && !daemon.database.IsExpired(reference) { log.Println(self.name, "got reply to", reference, "but we don't have it") daemon.ask_for_article <- ArticleEntry{reference, newsgroup} } f := daemon.store.CreateTempFile(msgid) if f == nil { log.Println(self.name, "discarding", msgid, "we are already loading it") // discard io.Copy(ioutil.Discard, dr) } else { // write header err = writeMIMEHeader(f, hdr) // write body _, err = io.Copy(f, dr) if err == nil || err == io.EOF { f.Close() // we gud, tell daemon daemon.infeed_load <- msgid } else { log.Println(self.name, "error reading message", err) } } conn.PrintfLine("235 We got it") } } else { // error here conn.PrintfLine("436 Transfer failed: " + err.Error()) } } } else if cmd == "NEWSGROUPS" { // handle NEWSGROUPS conn.PrintfLine("231 List of newsgroups follow") dw := conn.DotWriter() // get a list of every newsgroup groups := daemon.database.GetAllNewsgroups() // for each group for _, group := range groups { // get low/high water mark lo, hi, err := daemon.database.GetLastAndFirstForGroup(group) if err == nil { // XXX: we ignore errors here :\ _, _ = io.WriteString(dw, fmt.Sprintf("%s %d %d y\n", group, lo, hi)) } else { log.Println(self.name, "could not get low/high water mark for", group, err) } } // flush dotwriter dw.Close() } else if cmd == "XOVER" { // handle XOVER if self.group == "" { conn.PrintfLine("412 No newsgroup selected") } else { // handle xover command // right now it's every article in group models, err := daemon.database.GetPostsInGroup(self.group) if err == nil { conn.PrintfLine("224 Overview information follows") dw := conn.DotWriter() for idx, model := range models { io.WriteString(dw, fmt.Sprintf("%.6d\t%s\t\"%s\" <%s@%s>\t%s\t%s\t%s\r\n", idx+1, model.Subject(), model.Name(), model.Name(), model.Frontend(), model.Date(), model.MessageID(), model.Reference())) } dw.Close() } else { log.Println(self.name, "error when getting posts in", self.group, err) conn.PrintfLine("500 error, %s", err.Error()) } } } else if cmd == "GROUP" { // handle GROUP command group := parts[1] // check for newsgroup if daemon.database.HasNewsgroup(group) { // we have the group self.group = group // count posts number := daemon.database.CountPostsInGroup(group, 0) // get hi/low water marks low, hi, err := daemon.database.GetLastAndFirstForGroup(group) if err == nil { // we gud conn.PrintfLine("211 %d %d %d %s", number, low, hi, group) } else { // wtf error log.Println(self.name, "error in GROUP command", err) // still have to reply, send it bogus low/hi conn.PrintfLine("211 %d 0 1 %s", number, group) } } else { // no such group conn.PrintfLine("411 No Such Newsgroup") } } else { log.Println(self.name, "invalid command recv'd", cmd) conn.PrintfLine("500 Invalid command: %s", cmd) } } } return }