Пример #1
0
// 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
}
Пример #2
0
// 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
}
Пример #3
0
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()
}
Пример #4
0
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
}
Пример #5
0
// 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
}
Пример #6
0
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" {
		dw := c.DotWriter()
		defer dw.Close()
		return handleListOverviewFmt(dw, 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
}
Пример #7
0
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
}
Пример #8
0
// outbound setup, check capabilities and set mode
// returns (supports stream, supports reader) + error
func (self nntpConnection) outboundHandshake(conn *textproto.Conn) (stream, reader bool, err error) {
	log.Println(self.name, "outbound handshake")
	var code int
	var line string
	for err == nil {
		code, line, err = conn.ReadCodeLine(-1)
		log.Println(self.name, line)
		if err == nil {
			if code == 200 {
				// send capabilities
				log.Println(self.name, "ask for capabilities")
				err = conn.PrintfLine("CAPABILITIES")
				if err == nil {
					// read response
					dr := conn.DotReader()
					r := bufio.NewReader(dr)
					for {
						line, err = r.ReadString('\n')
						if err == io.EOF {
							// we are at the end of the dotreader
							// set err back to nil and break out
							err = nil
							break
						} else if err == nil {
							// we got a line
							if line == "MODE-READER\n" || line == "READER\n" {
								log.Println(self.name, "supports READER")
								reader = true
							} else if line == "STREAMING\n" {
								stream = true
								log.Println(self.name, "supports STREAMING")
							} else if line == "POSTIHAVESTREAMING\n" {
								stream = true
								reader = false
								log.Println(self.name, "is SRNd")
							}
						} else {
							// we got an error
							log.Println("error reading capabilities", err)
							break
						}
					}
					// return after reading
					return
				}
			} else if code == 201 {
				log.Println("feed", self.name, "does not allow posting")
				// we don't do auth yet
				break
			} else {
				continue
			}
		}
	}
	return
}
Пример #9
0
// 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
}
Пример #10
0
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
}
Пример #11
0
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
}
Пример #12
0
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
}
Пример #13
0
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
}
Пример #14
0
// 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) {
	// 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 = readMIMEHeader(conn.R)
		if err == nil {
			// prepare to read body
			dr := conn.DotReader()
			// check header and decide if we want this
			reason, ban, err := self.checkMIMEHeaderNoAuth(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)
					if ban {
						daemon.database.BanArticle(msgid, reason)
					}
				} else {
					// yeh we want it open up a file to store it in
					err = self.storeMessage(daemon, hdr, dr)
					if err != nil {
						log.Println(self.name, "failed to obtain article", err)
						// probably an invalid signature or format
						daemon.database.BanArticle(msgid, err.Error())
					}
				}
			} 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:
	} else {
		// invalid response
		log.Println(self.name, "invald response to ARTICLE:", code, line)
	}
	return
}
Пример #15
0
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
}
Пример #16
0
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
}
Пример #17
0
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
}
Пример #18
0
// 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
}
Пример #19
0
// 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
}
Пример #20
0
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
}
Пример #21
0
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
}
Пример #22
0
func handleOver(args []string, s *session, c *textproto.Conn) error {
	if s.group == nil {
		return NoGroupSelected
	}
	from, to := parseRange(args[0])
	articles, err := s.backend.GetArticles(s.group, from, to)
	if err != nil {
		return err
	}
	c.PrintfLine("224 here it comes")
	dw := c.DotWriter()
	defer dw.Close()
	for _, a := range articles {
		fmt.Fprintf(dw, "%d\t%s\t%s\t%s\t%s\t%s\t%d\t%d\n", a.Num,
			a.Article.Header.Get("Subject"),
			a.Article.Header.Get("From"),
			a.Article.Header.Get("Date"),
			a.Article.Header.Get("Message-Id"),
			a.Article.Header.Get("References"),
			a.Article.Bytes, a.Article.Lines)
	}
	return nil
}
Пример #23
0
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
}
Пример #24
0
func (s *server) handleConnection(p *textproto.Conn) {
	id := p.Next()
	p.StartRequest(id)
	p.EndRequest(id)
	p.StartResponse(id)
	p.PrintfLine("OK MPD gompd0.1")
	p.EndResponse(id)

	endIdle := make(chan bool)
	inIdle := false
	defer p.Close()
	for {
		id := p.Next()
		p.StartRequest(id)
		req, err := s.readRequest(p)
		if err != nil {
			return
		}
		// We need to do this inside request because idle response
		// may not have ended yet, but it will end after the following.
		if inIdle {
			endIdle <- true
		}
		p.EndRequest(id)

		if req.typ == idle {
			inIdle = true
			go s.writeIdleResponse(p, id, endIdle, req.args[1:])
			// writeIdleResponse does it's own StartResponse/EndResponse
			continue
		}

		p.StartResponse(id)
		if inIdle {
			inIdle = false
		}
		switch req.typ {
		case noIdle:
		case commandListOk:
			var ok, closed bool
			ok = true
			for _, args := range req.cmdList {
				ok, closed = s.writeResponse(p, args, "list_OK")
				if closed {
					return
				}
				if !ok {
					break
				}
			}
			if ok {
				p.PrintfLine("OK")
			}
		case simple:
			if _, closed := s.writeResponse(p, req.args, "OK"); closed {
				return
			}
		}
		p.EndResponse(id)
	}
}
Пример #25
0
// run the mainloop for this connection
// stream if true means they support streaming mode
// reader if true means they support reader mode
func (self nntpConnection) runConnection(daemon NNTPDaemon, inbound, stream, reader bool, preferMode string, conn *textproto.Conn) {

	var err error
	var line string
	var success bool

	for err == nil {
		if self.mode == "" {
			if inbound {
				// no mode and inbound
				line, err = conn.ReadLine()
				log.Println(self.name, line)
				parts := strings.Split(line, " ")
				cmd := parts[0]
				if cmd == "CAPABILITIES" {
					// write capabilities
					conn.PrintfLine("101 i support to the following:")
					dw := conn.DotWriter()
					caps := []string{"VERSION 2", "READER", "STREAMING", "IMPLEMENTATION srndv2"}
					for _, cap := range caps {
						io.WriteString(dw, cap)
						io.WriteString(dw, "\n")
					}
					dw.Close()
					log.Println(self.name, "sent Capabilities")
				} else if cmd == "MODE" {
					if len(parts) == 2 {
						if parts[1] == "READER" {
							// set reader mode
							self.mode = "READER"
							// posting is not permitted with reader mode
							conn.PrintfLine("201 Posting not permitted")
						} else if parts[1] == "STREAM" {
							// set streaming mode
							conn.PrintfLine("203 Stream it brah")
							self.mode = "STREAM"
							log.Println(self.name, "streaming enabled")
							go self.startStreaming(daemon, reader, conn)
						}
					}
				} else {
					// handle a it as a command, we don't have a mode set
					parts := strings.Split(line, " ")
					var code64 int64
					code64, err = strconv.ParseInt(parts[0], 10, 32)
					if err == nil {
						err = self.handleLine(daemon, int(code64), line[4:], conn)
					} else {
						err = self.handleLine(daemon, 0, line, conn)
					}
				}
			} else { // no mode and outbound
				if preferMode == "stream" {
					// try outbound streaming
					if stream {
						success, err = self.modeSwitch("STREAM", conn)
						self.mode = "STREAM"
						if success {
							// start outbound streaming in background
							go self.startStreaming(daemon, reader, conn)
						}
					}
				} else if reader {
					// try reader mode
					success, err = self.modeSwitch("READER", conn)
					if success {
						self.mode = "READER"
						self.startReader(daemon, conn)
					}
				}
				if success {
					log.Println(self.name, "mode set to", self.mode)
				} else {
					// bullshit
					// we can't do anything so we quit
					log.Println(self.name, "can't stream or read, wtf?")
					conn.PrintfLine("QUIT")
					conn.Close()
					return
				}
			}
		} else {
			// we have our mode set
			line, err = conn.ReadLine()
			if err == nil {
				parts := strings.Split(line, " ")
				var code64 int64
				code64, err = strconv.ParseInt(parts[0], 10, 32)
				if err == nil {
					err = self.handleLine(daemon, int(code64), line[4:], conn)
				} else {
					err = self.handleLine(daemon, 0, line, conn)
				}
			}
		}
	}
	log.Println(self.name, "got error", err)
	if !inbound {
		// send quit on outbound
		conn.PrintfLine("QUIT")
	}
	conn.Close()
}
Пример #26
0
// send a banner for inbound connections
func (self nntpConnection) inboundHandshake(conn *textproto.Conn) (err error) {
	err = conn.PrintfLine("200 Posting Allowed")
	return err
}
Пример #27
0
func (s *server) writeResponse(p *textproto.Conn, args []string, okLine string) (cmdOk, closed bool) {
	if len(args) < 1 {
		p.PrintfLine("No command given")
		return
	}
	ack := func(format string, a ...interface{}) error {
		return p.PrintfLine("ACK {"+args[0]+"} "+format, a...)
	}
	switch args[0] {
	case "close":
		closed = true
		return
	case "list":
		if len(args) < 2 {
			ack("too few arguments")
			return
		}
		if args[1] == "file" {
			for _, a := range s.database {
				p.PrintfLine("file: %s", a["file"])
			}
		}
	case "lsinfo":
		for _, a := range s.database {
			p.PrintfLine("file: %s", a["file"])
			p.PrintfLine("Last-Modified: 2014-07-02T12:32:26Z")
			p.PrintfLine("Artist: Newcleus")
			p.PrintfLine("Title: Jam On It")
			p.PrintfLine("Track: 02")
		}
		for _, a := range []string{
			"music/Buck 65 - Dirtbike 1",
			"music/Howlin' Wolf - Moanin' in the Moonlight",
		} {
			p.PrintfLine("directory: %s", a)
		}
		p.PrintfLine("playlist: BBC 6 Music.m3u")
	case "listplaylists":
		for k := range s.playlists {
			p.PrintfLine("playlist: %s", k)
		}
	case "playlistinfo":
		var rng []string
		var start int
		end := s.currentPlaylist.Len()

		if len(args) >= 2 {
			rng = strings.Split(args[1], ":")
		}

		if len(rng) == 1 {
			// Requesting a single song from the playlist at position i.
			i, err := strconv.Atoi(rng[0])
			if err != nil {
				ack("invalid song position")
				return
			}
			start = i
			end = i + 1
		} else if len(rng) == 2 {
			// Requesting a range of the playlist from specified start/end positions.
			var err error
			start, err = strconv.Atoi(rng[0])
			if err != nil {
				ack("Integer or range expected")
				return
			}
			end, err := strconv.Atoi(rng[1])
			if err != nil {
				ack("Integer or range expected")
				return
			}
			if start < 0 || end < 0 {
				ack("Number is negative")
				return
			}
		}

		for i := start; i < end; i++ {
			p.PrintfLine("file: %s", s.database[s.currentPlaylist.At(i)]["file"])
		}
	case "listplaylistinfo":
		if len(args) < 2 {
			ack("too few arguments")
			return
		}
		pl, ok := s.playlists[args[1]]
		if !ok {
			ack("no such playlist")
			return
		}
		for i := 0; i < pl.Len(); i++ {
			p.PrintfLine("file: %s", s.database[pl.At(i)]["file"])
		}
	case "playlistadd":
		if len(args) != 3 {
			ack("wrong number of arguments")
			return
		}
		name, uri := args[1], args[2]
		i, ok := s.index[uri]
		if !ok {
			ack("URI not found")
			return
		}
		if s.playlists[name] == nil {
			s.playlists[name] = newPlaylist()
		}
		s.playlists[name].Add(i)
	case "playlistdelete":
		if len(args) != 3 {
			ack("wrong number of arguments")
			return
		}
		name := args[1]
		pos, err := strconv.Atoi(args[2])
		if err != nil {
			ack("invalid position number")
			return
		}
		pl, ok := s.playlists[name]
		if !ok {
			ack("playlist not found")
			return
		}
		if pos < 0 || pos >= pl.Len() {
			ack("invalid song position")
			return
		}
		pl.Delete(pos)
	case "playlistclear":
		if len(args) != 2 {
			ack("wrong number of arguments")
			return
		}
		pl, ok := s.playlists[args[1]]
		if !ok {
			ack("playlist not found")
			return
		}
		pl.Clear()
	case "rm":
		if len(args) != 2 {
			ack("wrong number of arguments")
			return
		}
		_, ok := s.playlists[args[1]]
		if !ok {
			ack("playlist not found")
			return
		}
		delete(s.playlists, args[1])
	case "rename":
		if len(args) != 3 {
			ack("wrong number of arguments")
			return
		}
		old, new := args[1], args[2]
		_, ok := s.playlists[old]
		if !ok {
			ack("playlist %s does not exist", old)
			return
		}
		_, ok = s.playlists[new]
		if ok {
			ack("playlist %s already exists", new)
			return
		}
		s.playlists[new] = s.playlists[old]
		delete(s.playlists, old)
	case "load":
		if len(args) != 2 {
			ack("wrong number of arguments")
			return
		}
		pl, ok := s.playlists[args[1]]
		if !ok {
			ack("playlist %s does not exist", args[1])
			return
		}
		s.currentPlaylist.Append(pl)
	case "clear":
		s.currentPlaylist.Clear()
	case "add":
		if len(args) != 2 {
			ack("wrong number of arguments")
			return
		}
		i, ok := s.index[args[1]]
		if !ok {
			ack("URI not found")
			return
		}
		s.currentPlaylist.Add(i)
	case "delete":
		if len(args) != 2 {
			ack("wrong number of arguments")
			return
		}
		i, err := strconv.Atoi(args[1])
		if err != nil {
			ack("invalid song position")
			return
		}
		s.idleEventc <- "playlist"
		if i < 0 || i >= s.currentPlaylist.Len() {
			ack("invalid song position")
			return
		}
		s.currentPlaylist.Delete(i)
	case "save":
		if len(args) != 2 {
			ack("wrong number of arguments")
			return
		}
		name := args[1]
		_, ok := s.playlists[name]
		if ok {
			ack("playlist %s already exists", name)
			return
		}
		s.playlists[name] = newPlaylist()
		s.playlists[name].Append(s.currentPlaylist)
	case "play", "stop":
		s.idleEventc <- "player"
		s.state = args[0]
	case "next":
		s.idleEventc <- "player"
		if s.pos < 0 || s.pos >= s.currentPlaylist.Len() {
			s.pos = 0
			break
		}
		s.pos = (s.pos + 1) % s.currentPlaylist.Len()
	case "previous":
		s.idleEventc <- "player"
		if s.pos < 0 || s.pos >= s.currentPlaylist.Len() {
			s.pos = 0
			break
		}
		if s.pos == 0 {
			s.pos = s.currentPlaylist.Len() - 1
			break
		}
		s.pos--
	case "pause":
		if s.state != "stop" {
			s.state = args[0]
		}
	case "status":
		state := s.state
		p.PrintfLine("state: %s", state)
	case "update":
		p.PrintfLine("updating_db: 1")
	case "ping":
	case "currentsong":
		if s.currentPlaylist.Len() == 0 {
			break
		}
		if s.pos >= s.currentPlaylist.Len() {
			s.pos = 0
		}
		p.PrintfLine("file: %s", s.database[s.currentPlaylist.At(s.pos)]["file"])
	case "outputs":
		p.PrintfLine("outputid: 0")
		p.PrintfLine("outputenabled: 1")
		p.PrintfLine("outputname: downstairs")
		p.PrintfLine("outputid: 1")
		p.PrintfLine("outputenabled: 0")
		p.PrintfLine("outputname: upstairs")
	case "disableoutput", "enableoutput":
	default:
		p.PrintfLine("ACK {} unknown command %q", args[0])
		log.Printf("unknown command: %s\n", args[0])
		return
	}
	cmdOk = true
	p.PrintfLine(okLine)
	return
}
Пример #28
0
func childError(t *testing.T, conn *textproto.Conn, err error) {
	conn.PrintfLine("E")
	t.Error(err)
	os.Exit(1)
}
Пример #29
0
func handleNewGroups(args []string, s *session, c *textproto.Conn) error {
	c.PrintfLine("231 list of newsgroups follows")
	c.PrintfLine(".")
	return nil
}
Пример #30
0
func handleQuit(args []string, s *session, c *textproto.Conn) error {
	c.PrintfLine("205 bye")
	return io.EOF
}