diff --git a/src/irc/channel.go b/src/irc/channel.go index c33f112a..10d80c0f 100644 --- a/src/irc/channel.go +++ b/src/irc/channel.go @@ -89,14 +89,18 @@ func (channel *Channel) GetTopic(replier Replier) { replier.Replies() <- RplTopic(channel) } -func (channel *Channel) Id() string { +func (channel Channel) Id() string { return channel.name } -func (channel *Channel) PublicId() string { +func (channel Channel) PublicId() string { return channel.name } +func (channel Channel) Commands() chan<- ChannelCommand { + return channel.commands +} + // // commands // diff --git a/src/irc/client.go b/src/irc/client.go index 9394fd44..f507c205 100644 --- a/src/irc/client.go +++ b/src/irc/client.go @@ -7,11 +7,6 @@ import ( "time" ) -type Replier interface { - Identifier - Replies() chan<- Reply -} - type Client struct { conn net.Conn username string @@ -53,7 +48,7 @@ func (c *Client) readConn(recv <-chan string) { m, err := ParseCommand(str) if err != nil { - c.replies <- ErrNeedMoreParams(c.server, str) + c.Replies() <- ErrNeedMoreParams(c.server, str) continue } @@ -70,15 +65,15 @@ func (c *Client) writeConn(write chan<- string, replies <-chan Reply) { } } -func (c *Client) Replies() chan<- Reply { +func (c Client) Replies() chan<- Reply { return c.replies } -func (c *Client) Server() *Server { +func (c Client) Server() *Server { return c.server } -func (c *Client) Nick() string { +func (c Client) Nick() string { if c.user != nil { return c.user.nick } @@ -90,33 +85,33 @@ func (c *Client) Nick() string { return "*" } -func (c *Client) UModeString() string { +func (c Client) UModeString() string { return "" } -func (c *Client) HasNick() bool { +func (c Client) HasNick() bool { return c.nick != "" } -func (c *Client) HasUser() bool { +func (c Client) HasUser() bool { return c.username != "" } -func (c *Client) Username() string { +func (c Client) Username() string { if c.HasUser() { return c.username } return "*" } -func (c *Client) UserHost() string { +func (c Client) UserHost() string { return fmt.Sprintf("%s!%s@%s", c.Nick(), c.Username(), c.hostname) } -func (c *Client) Id() string { +func (c Client) Id() string { return c.UserHost() } -func (c *Client) PublicId() string { +func (c Client) PublicId() string { return fmt.Sprintf("%s!%s@%s", c.Nick(), c.Nick(), c.server.Id()) } diff --git a/src/irc/commands.go b/src/irc/commands.go index a08b2ce8..27c4dd91 100644 --- a/src/irc/commands.go +++ b/src/irc/commands.go @@ -6,8 +6,32 @@ import ( "strings" ) +type Command interface { + Client() *Client + HandleServer(*Server) +} + +type EditableCommand interface { + Command + SetClient(*Client) +} + var ( NotEnoughArgsError = errors.New("not enough arguments") + ErrParseCommand = errors.New("failed to parse message") + parseCommandFuncs = map[string]func([]string) (EditableCommand, error){ + "JOIN": NewJoinCommand, + "MODE": NewModeCommand, + "NICK": NewNickCommand, + "PART": NewPartCommand, + "PASS": NewPassCommand, + "PING": NewPingCommand, + "PONG": NewPongCommand, + "PRIVMSG": NewPrivMsgCommand, + "QUIT": NewQuitCommand, + "TOPIC": NewTopicCommand, + "USER": NewUserMsgCommand, + } ) type BaseCommand struct { @@ -25,28 +49,6 @@ func (base *BaseCommand) SetClient(c *Client) { base.client = c } -type EditableCommand interface { - Command - SetClient(*Client) -} - -var ( - ErrParseCommand = errors.New("failed to parse message") - parseCommandFuncs = map[string]func([]string) (EditableCommand, error){ - "JOIN": NewJoinCommand, - "MODE": NewModeCommand, - "NICK": NewNickCommand, - "PART": NewPartCommand, - "PASS": NewPassCommand, - "PING": NewPingCommand, - "PONG": NewPongCommand, - "PRIVMSG": NewPrivMsgCommand, - "QUIT": NewQuitCommand, - "TOPIC": NewTopicCommand, - "USER": NewUserMsgCommand, - } -) - func ParseCommand(line string) (EditableCommand, error) { command, args := parseLine(line) constructor := parseCommandFuncs[command] diff --git a/src/irc/reply.go b/src/irc/reply.go index 14c05666..db09493b 100644 --- a/src/irc/reply.go +++ b/src/irc/reply.go @@ -12,6 +12,11 @@ type Identifier interface { Nick() string } +type Replier interface { + Identifier + Replies() chan<- Reply +} + type Reply interface { String(client *Client) string Source() Identifier @@ -30,24 +35,24 @@ func NewBasicReply(source Identifier, code string, return &BasicReply{source, code, fullMessage} } -func (reply *BasicReply) String(client *Client) string { +func (reply BasicReply) String(client *Client) string { return reply.message } -func (reply *BasicReply) Source() Identifier { +func (reply BasicReply) Source() Identifier { return reply.source } type NumericReply struct { - *BasicReply + BasicReply } func NewNumericReply(source Identifier, code string, format string, args ...interface{}) *NumericReply { - return &NumericReply{&BasicReply{source, code, fmt.Sprintf(format, args...)}} + return &NumericReply{BasicReply{source, code, fmt.Sprintf(format, args...)}} } -func (reply *NumericReply) String(client *Client) string { +func (reply NumericReply) String(client *Client) string { return fmt.Sprintf(":%s %s %s %s\r\n", reply.source.Id(), reply.code, client.Nick(), reply.message) } diff --git a/src/irc/server.go b/src/irc/server.go index 63bff3cb..e99d28b1 100644 --- a/src/irc/server.go +++ b/src/irc/server.go @@ -12,11 +12,6 @@ type ChannelNameMap map[string]*Channel type UserNameMap map[string]*User type ServiceNameMap map[string]*Service -type Command interface { - Client() *Client - HandleServer(*Server) -} - type Server struct { hostname string ctime time.Time @@ -83,7 +78,7 @@ func (s *Server) GetOrMakeChannel(name string) *Channel { } // Send a message to clients of channels fromClient is a member. -func (s *Server) InterestedUsers(fromUser *User) UserSet { +func (s Server) InterestedUsers(fromUser *User) UserSet { users := make(UserSet) users.Add(fromUser) for channel := range fromUser.channels { @@ -102,20 +97,20 @@ func (s *Server) tryRegister(c *Client) { c.registered = true replies := []Reply{RplWelcome(s, c), RplYourHost(s, c), RplCreated(s), RplMyInfo(s)} for _, reply := range replies { - c.replies <- reply + c.Replies() <- reply } } } -func (s *Server) Id() string { +func (s Server) Id() string { return s.hostname } -func (s *Server) PublicId() string { +func (s Server) PublicId() string { return s.Id() } -func (s *Server) Nick() string { +func (s Server) Nick() string { return s.name } @@ -128,11 +123,11 @@ func (s *Server) DeleteChannel(channel *Channel) { // func (m *UnknownCommand) HandleServer(s *Server) { - m.Client().replies <- ErrUnknownCommand(s, m.command) + m.Client().Replies() <- ErrUnknownCommand(s, m.command) } func (m *PingCommand) HandleServer(s *Server) { - m.Client().replies <- RplPong(s) + m.Client().Replies() <- RplPong(s) } func (m *PongCommand) HandleServer(s *Server) { @@ -142,7 +137,7 @@ func (m *PongCommand) HandleServer(s *Server) { func (m *PassCommand) HandleServer(s *Server) { err := bcrypt.CompareHashAndPassword(s.password, []byte(m.password)) if err != nil { - m.Client().replies <- ErrPasswdMismatch(s) + m.Client().Replies() <- ErrPasswdMismatch(s) return } @@ -153,19 +148,19 @@ func (m *PassCommand) HandleServer(s *Server) { func (m *NickCommand) HandleServer(s *Server) { c := m.Client() if c.user == nil { - c.replies <- RplNick(c, m.nickname) + c.Replies() <- RplNick(c, m.nickname) c.nick = m.nickname s.tryRegister(c) return } - c.user.replies <- ErrNoPrivileges(s) + c.user.Replies() <- ErrNoPrivileges(s) } func (m *UserMsgCommand) HandleServer(s *Server) { c := m.Client() if c.username != "" { - c.replies <- ErrAlreadyRegistered(s) + c.Replies() <- ErrAlreadyRegistered(s) return } @@ -180,7 +175,7 @@ func (m *QuitCommand) HandleServer(s *Server) { if user != nil { reply := RplQuit(c, m.message) for user := range s.InterestedUsers(c.user) { - user.replies <- reply + user.Replies() <- reply } } c.conn.Close() @@ -194,7 +189,7 @@ func (m *QuitCommand) HandleServer(s *Server) { BaseCommand: BaseCommand{c}, } for channel := range user.channels { - channel.commands <- cmd + channel.Commands() <- cmd } } } @@ -204,7 +199,7 @@ func (m *JoinCommand) HandleServer(s *Server) { if c.user == nil { for name := range m.channels { - c.replies <- ErrNoSuchChannel(s, name) + c.Replies() <- ErrNoSuchChannel(s, name) } return } @@ -214,13 +209,13 @@ func (m *JoinCommand) HandleServer(s *Server) { BaseCommand: BaseCommand{c}, } for channel := range c.user.channels { - channel.commands <- cmd + channel.Commands() <- cmd } return } for name := range m.channels { - s.GetOrMakeChannel(name).commands <- m + s.GetOrMakeChannel(name).Commands() <- m } } @@ -229,7 +224,7 @@ func (m *PartCommand) HandleServer(s *Server) { if user == nil { for _, chname := range m.channels { - m.Client().replies <- ErrNoSuchChannel(s, chname) + m.Client().Replies() <- ErrNoSuchChannel(s, chname) } return } @@ -238,11 +233,11 @@ func (m *PartCommand) HandleServer(s *Server) { channel := s.channels[chname] if channel == nil { - user.replies <- ErrNoSuchChannel(s, channel.name) + user.Replies() <- ErrNoSuchChannel(s, channel.name) continue } - channel.commands <- m + channel.Commands() <- m } } @@ -250,52 +245,52 @@ func (m *TopicCommand) HandleServer(s *Server) { user := m.Client().user if user == nil { - m.Client().replies <- ErrNoSuchChannel(s, m.channel) + m.Client().Replies() <- ErrNoSuchChannel(s, m.channel) return } channel := s.channels[m.channel] if channel == nil { - user.replies <- ErrNoSuchChannel(s, m.channel) + user.Replies() <- ErrNoSuchChannel(s, m.channel) return } - channel.commands <- m + channel.Commands() <- m } func (m *PrivMsgCommand) HandleServer(s *Server) { service := s.services[m.target] if service != nil { - service.commands <- m + service.Commands() <- m return } user := m.Client().user if user == nil { - m.Client().replies <- ErrNoSuchNick(s, m.target) + m.Client().Replies() <- ErrNoSuchNick(s, m.target) return } if m.TargetIsChannel() { channel := s.channels[m.target] if channel == nil { - user.replies <- ErrNoSuchChannel(s, m.target) + user.Replies() <- ErrNoSuchChannel(s, m.target) return } - channel.commands <- m + channel.Commands() <- m return } target := s.users[m.target] if target == nil { - user.replies <- ErrNoSuchNick(s, m.target) + user.Replies() <- ErrNoSuchNick(s, m.target) return } - target.commands <- m + target.Commands() <- m } func (m *ModeCommand) HandleServer(s *Server) { - m.Client().replies <- RplUModeIs(s, m.Client()) + m.Client().Replies() <- RplUModeIs(s, m.Client()) } diff --git a/src/irc/service.go b/src/irc/service.go index b1dba229..49e1af13 100644 --- a/src/irc/service.go +++ b/src/irc/service.go @@ -28,8 +28,7 @@ func NewService(s *Server, name string) *Service { return service } -func (service *Service) HandleMsg(m *PrivMsgCommand) { -} +func (service *Service) HandleMsg(m *PrivMsgCommand) {} func (service *Service) receiveCommands(commands <-chan ServiceCommand) { for command := range commands { @@ -38,20 +37,24 @@ func (service *Service) receiveCommands(commands <-chan ServiceCommand) { } } -func (service *Service) Id() string { +func (service Service) Id() string { return fmt.Sprintf("%s!%s@%s", service.name, service.name, service.server.name) } -func (service *Service) PublicId() string { +func (service Service) PublicId() string { return service.Id() } -func (service *Service) Nick() string { +func (service Service) Nick() string { return service.name } func (service *Service) Reply(client *Client, message string) { - client.replies <- RplPrivMsg(service, client, message) + client.Replies() <- RplPrivMsg(service, client, message) +} + +func (service Service) Commands() chan<- ServiceCommand { + return service.commands } // diff --git a/src/irc/user.go b/src/irc/user.go index 1884a151..447e4745 100644 --- a/src/irc/user.go +++ b/src/irc/user.go @@ -32,25 +32,29 @@ func (set UserSet) Remove(user *User) { } func NewUser(nick string, password string, server *Server) *User { - hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) - if err != nil { - panic("bcrypt failed; cannot generate password hash") - } commands := make(chan UserCommand) replies := make(chan Reply) user := &User{ nick: nick, - hash: hash, server: server, clients: make(ClientSet), channels: make(ChannelSet), replies: replies, } + user.SetPassword(password) go user.receiveCommands(commands) go user.receiveReplies(replies) return user } +func (user *User) SetPassword(password string) { + hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + if err != nil { + panic("bcrypt failed; cannot generate password hash") + } + user.hash = hash +} + func (user *User) receiveCommands(commands <-chan UserCommand) { for command := range commands { log.Printf("%s %T %+v", user.Id(), command, command) @@ -63,25 +67,29 @@ func (user *User) receiveReplies(replies <-chan Reply) { for reply := range replies { log.Printf("%s %T %+v", user.Id(), reply, reply) for client := range user.clients { - client.replies <- reply + client.Replies() <- reply } } } // Identifier -func (user *User) Id() string { +func (user User) Id() string { return fmt.Sprintf("%s!%s@%s", user.nick, user.nick, user.server.Id()) } -func (user *User) PublicId() string { +func (user User) PublicId() string { return user.Id() } -func (user *User) Nick() string { +func (user User) Nick() string { return user.nick } +func (user User) Commands() chan<- UserCommand { + return user.commands +} + func (user *User) Login(c *Client, nick string, password string) bool { if nick != c.nick { return false @@ -93,7 +101,7 @@ func (user *User) Login(c *Client, nick string, password string) bool { err := bcrypt.CompareHashAndPassword(user.hash, []byte(password)) if err != nil { - c.replies <- ErrNoPrivileges(user.server) + c.Replies() <- ErrNoPrivileges(user.server) return false } @@ -101,8 +109,8 @@ func (user *User) Login(c *Client, nick string, password string) bool { c.user = user for channel := range user.channels { channel.GetTopic(c) - c.replies <- RplNamReply(channel) - c.replies <- RplEndOfNames(channel.server) + c.Replies() <- RplNamReply(channel) + c.Replies() <- RplEndOfNames(channel.server) } return true } @@ -115,11 +123,11 @@ func (user *User) LogoutClient(c *Client) bool { return false } -func (user *User) HasClients() bool { +func (user User) HasClients() bool { return len(user.clients) > 0 } -func (user *User) Replies() chan<- Reply { +func (user User) Replies() chan<- Reply { return user.replies } @@ -128,5 +136,5 @@ func (user *User) Replies() chan<- Reply { // func (m *PrivMsgCommand) HandleUser(user *User) { - user.replies <- RplPrivMsg(m.Client(), user, m.message) + user.Replies() <- RplPrivMsg(m.Client(), user, m.message) }