Implement mode [+-]i.

This commit is contained in:
Jeremy Latt 2012-04-17 20:24:26 -07:00
parent 8f0bd1b07a
commit 99364e8b5f
7 changed files with 94 additions and 29 deletions

View File

@ -5,18 +5,19 @@ import (
)
type Client struct {
addr net.Addr
conn net.Conn
send chan<- string
recv <-chan string
username string
realname string
nick string
registered bool
invisible bool
}
func NewClient(conn net.Conn) *Client {
client := new(Client)
client.addr = conn.RemoteAddr()
client.conn = conn
client.send = StringWriteChan(conn)
client.recv = StringReadChan(conn)
return client
@ -38,3 +39,10 @@ func (c *Client) Nick() string {
}
return "<guest>"
}
func (c *Client) UModeString() string {
if c.invisible {
return "+i"
}
return ""
}

View File

@ -1,5 +1,9 @@
package irc
type Message interface {
Handle(s *Server, c *Client)
}
func (m *NickMessage) Handle(s *Server, c *Client) {
if s.nicks[m.nickname] != nil {
c.send <- ErrNickNameInUse(m.nickname)
@ -24,6 +28,7 @@ func (m *UserMessage) Handle(s *Server, c *Client) {
func (m *QuitMessage) Handle(s *Server, c *Client) {
c.send <- MessageError()
c.conn.Close()
delete(s.nicks, c.nick)
}
@ -35,6 +40,21 @@ func (m *PingMessage) Handle(s *Server, c *Client) {
c.send <- MessagePong()
}
func (m *ModeMessage) Handle(s *Server, c *Client) {
if m.nickname != c.nick {
c.send <- ErrUsersDontMatch(c.Nick())
return
}
for _, mode := range m.modes {
if mode == "+i" {
c.invisible = true
} else if mode == "-i" {
c.invisible = false
}
}
c.send <- ReplyUModeIs(c)
}
func tryRegister(s *Server, c *Client) {
if (!c.registered && c.nick != "" && c.username != "") {
c.registered = true

View File

@ -10,6 +10,7 @@ const (
RPL_YOURHOST = "002"
RPL_CREATED = "003"
RPL_MYINFO = "004"
RPL_UMODEIS = "221"
RPL_NONE = "300"
)

View File

@ -1,9 +1,5 @@
package irc
type Message interface {
Handle(s *Server, c *Client)
}
type NickMessage struct {
nickname string
}
@ -15,7 +11,6 @@ type UserMessage struct {
realname string
}
type QuitMessage struct {
message string
}
@ -25,3 +20,8 @@ type UnknownMessage struct {
}
type PingMessage struct {}
type ModeMessage struct {
nickname string
modes []string
}

View File

@ -16,12 +16,13 @@ func readTrimmedLine(reader *bufio.Reader) (string, error) {
func StringReadChan(conn net.Conn) <-chan string {
ch := make(chan string)
reader := bufio.NewReader(conn)
addr := conn.RemoteAddr()
go func() {
for {
line, err := readTrimmedLine(reader)
if (line != "") {
ch <- line
log.Printf("%s -> %s", conn.RemoteAddr(), line)
log.Printf("%s -> %s", addr, line)
}
if err != nil {
break
@ -35,13 +36,14 @@ func StringReadChan(conn net.Conn) <-chan string {
func StringWriteChan(conn net.Conn) chan<- string {
ch := make(chan string)
writer := bufio.NewWriter(conn)
addr := conn.RemoteAddr()
go func() {
for str := range ch {
if _, err := writer.WriteString(str + "\r\n"); err != nil {
break
}
writer.Flush()
log.Printf("%s <- %s", conn.RemoteAddr(), str)
log.Printf("%s <- %s", addr, str)
}
close(ch)
}()

View File

@ -1,10 +1,33 @@
package irc
import (
"fmt"
"regexp"
"strconv"
"strings"
)
var commands = map[string]func([]string) Message {
"MODE": NewModeMessage,
"NICK": NewNickMessage,
"PING": NewPingMessage,
"QUIT": NewQuitMessage,
"USER": NewUserMessage,
}
func ParseMessage(line string) Message {
command, args := parseLine(line)
constructor, ok := commands[command]
var msg Message
if ok {
msg = constructor(args)
}
if msg == nil {
msg = &UnknownMessage{command}
}
return msg
}
func parseArg(line string) (string, string) {
if line == "" {
return "", ""
@ -31,25 +54,6 @@ func parseLine(line string) (string, []string) {
return args[0], args[1:]
}
var commands = map[string]func([]string) Message {
"NICK": NewNickMessage,
"PING": NewPingMessage,
"QUIT": NewQuitMessage,
"USER": NewUserMessage,
}
func ParseMessage(line string) Message {
command, args := parseLine(line)
constructor, ok := commands[command]
var msg Message
if ok {
msg = constructor(args)
}
if msg == nil {
msg = &UnknownMessage{command}
}
return msg
}
// []string => Message constructors
@ -86,3 +90,25 @@ func NewUserMessage(args []string) Message {
msg.realname = args[3]
return msg
}
var MODE_RE = regexp.MustCompile("^[-+][a-zA-Z]+$")
func NewModeMessage(args []string) Message {
if len(args) < 1 {
return nil
}
msg := new(ModeMessage)
msg.nickname = args[0]
for _, arg := range args[1:] {
if !MODE_RE.MatchString(arg) {
// TODO invalid args
return nil
}
prefix := arg[0]
for _, c := range arg[1:] {
mode := fmt.Sprintf("%c%c", prefix, c)
msg.modes = append(msg.modes, mode)
}
}
return msg
}

View File

@ -17,7 +17,11 @@ func ReplyCreated(nick string, created string) string {
}
func ReplyMyInfo(nick string, servername string) string {
return fmt.Sprintf("%s %s %s %s <user modes> <channel modes>", RPL_MYINFO, nick, servername, VERSION)
return fmt.Sprintf("%s %s %s %s i <channel modes>", RPL_MYINFO, nick, servername, VERSION)
}
func ReplyUModeIs(c *Client) string {
return fmt.Sprintf("%s %s %s", RPL_UMODEIS, c.Nick(), c.UModeString())
}
func ErrAlreadyRegistered(nick string) string {
@ -32,6 +36,10 @@ func ErrUnknownCommand(nick string, command string) string {
return fmt.Sprintf("%s %s %s :Unknown command", ERR_UNKNOWNCOMMAND, nick, command)
}
func ErrUsersDontMatch(nick string) string {
return fmt.Sprintf("%s %s :Cannot change mode for other users", ERR_USERSDONTMATCH, nick)
}
func MessagePong() string {
return "PONG"
}