diff --git a/irc/channel.go b/irc/channel.go index fed224aa..ec309ede 100644 --- a/irc/channel.go +++ b/irc/channel.go @@ -5,15 +5,13 @@ import ( ) type Channel struct { - banList []UserMask - commands chan<- ChannelCommand - flags ChannelModeSet - key string - members MemberSet - name string - replies chan<- Reply - server *Server - topic string + banList []UserMask + flags ChannelModeSet + key string + members MemberSet + name string + server *Server + topic string } func IsChannel(target string) bool { @@ -30,65 +28,27 @@ func IsChannel(target string) bool { // NewChannel creates a new channel from a `Server` and a `name` // string, which must be unique on the server. func NewChannel(s *Server, name string) *Channel { - commands := make(chan ChannelCommand) - replies := make(chan Reply) channel := &Channel{ - banList: make([]UserMask, 0), - commands: commands, - flags: make(ChannelModeSet), - members: make(MemberSet), - name: name, - replies: replies, - server: s, + banList: make([]UserMask, 0), + flags: make(ChannelModeSet), + members: make(MemberSet), + name: name, + server: s, } - go channel.receiveCommands(commands) - go channel.receiveReplies(replies) return channel } -type DestroyChannel struct { - BaseCommand - channel *Channel -} - -func (channel *Channel) Destroy() { - channel.server.Command(&DestroyChannel{ - channel: channel, - }) - close(channel.commands) - close(channel.replies) -} - -func (channel *Channel) Command(command ChannelCommand) { - channel.commands <- command -} - func (channel *Channel) Reply(reply Reply) { - channel.replies <- reply -} - -func (channel *Channel) receiveCommands(commands <-chan ChannelCommand) { - for command := range commands { - if DEBUG_CHANNEL { - log.Printf("%s → %s %s", command.Source(), channel, command) - } - command.HandleChannel(channel) + if DEBUG_CHANNEL { + log.Printf("%s ← %s %s", channel, reply.Source(), reply) } -} -func (channel *Channel) receiveReplies(replies <-chan Reply) { - for reply := range replies { - if DEBUG_CHANNEL { - log.Printf("%s ← %s %s", channel, reply.Source(), reply) - } - - for client := range channel.members { - if (reply.Code() == ReplyCode(PRIVMSG)) && - (reply.Source() == Identifier(client)) { - continue - } - client.Reply(reply) + for client := range channel.members { + if (reply.Code() == ReplyCode(PRIVMSG)) && + (reply.Source() == Identifier(client)) { + continue } + client.Reply(reply) } } @@ -96,16 +56,6 @@ func (channel *Channel) IsEmpty() bool { return len(channel.members) == 0 } -func (channel *Channel) GetTopic(replier Replier) { - if channel.topic == "" { - // clients appear not to expect this - //replier.Reply(RplNoTopic(channel)) - return - } - - replier.Reply(RplTopic(channel)) -} - func (channel *Channel) GetUsers(replier Replier) { replier.Reply(NewNamesReply(channel)) } @@ -164,18 +114,8 @@ func (channel *Channel) ModeString() (str string) { return } -type JoinChannel struct { - BaseCommand - channel *Channel -} - -// -// commands -// - -func (m *JoinCommand) HandleChannel(channel *Channel) { - client := m.Client() - if (channel.key != "") && (channel.key != m.channels[channel.name]) { +func (channel *Channel) Join(client *Client, key string) { + if (channel.key != "") && (channel.key != key) { client.Reply(ErrBadChannelKey(channel)) return } @@ -186,18 +126,11 @@ func (m *JoinCommand) HandleChannel(channel *Channel) { channel.members[client][ChannelOperator] = true } - client.commands <- &JoinChannel{ - channel: channel, - } + client.channels.Add(channel) - addClient := &AddFriend{ - client: client, - } for member := range channel.members { - client.commands <- &AddFriend{ - client: member, - } - member.commands <- addClient + client.AddFriend(member) + member.AddFriend(client) } channel.Reply(RplJoin(client, channel)) @@ -205,77 +138,73 @@ func (m *JoinCommand) HandleChannel(channel *Channel) { channel.GetUsers(client) } -type PartChannel struct { - channel *Channel -} - -func (m *PartCommand) HandleChannel(channel *Channel) { - client := m.Client() - +func (channel *Channel) Part(client *Client, message string) { if !channel.members.Has(client) { client.Reply(ErrNotOnChannel(channel)) return } - channel.Reply(RplPart(client, channel, m.Message())) + channel.Reply(RplPart(client, channel, message)) channel.members.Remove(client) - client.commands <- &PartChannel{ - channel: channel, - } + client.channels.Remove(channel) + for member := range channel.members { - member.commands <- &RemoveFriend{ - client: client, - } + member.RemoveFriend(client) } if channel.IsEmpty() { - channel.Destroy() + channel.server.channels.Remove(channel) } } -func (m *TopicCommand) HandleChannel(channel *Channel) { - client := m.Client() - +func (channel *Channel) GetTopic(client *Client) { if !channel.members.Has(client) { client.Reply(ErrNotOnChannel(channel)) return } - if !m.setTopic { - channel.GetTopic(client) + if channel.topic == "" { + // clients appear not to expect this + //replier.Reply(RplNoTopic(channel)) return } - if channel.flags[OpOnlyTopic] { + client.Reply(RplTopic(channel)) +} + +func (channel *Channel) SetTopic(client *Client, topic string) { + if !channel.members.Has(client) { + client.Reply(ErrNotOnChannel(channel)) + return + } + + if channel.flags[OpOnlyTopic] && !channel.members[client][ChannelOperator] { client.Reply(ErrChanOPrivIsNeeded(channel)) return } - channel.topic = m.topic + channel.topic = topic channel.Reply(RplTopicMsg(client, channel)) } -func (m *PrivMsgCommand) HandleChannel(channel *Channel) { - client := m.Client() +func (channel *Channel) PrivMsg(client *Client, message string) { if channel.flags[NoOutside] && !channel.members.Has(client) { client.Reply(ErrCannotSendToChan(channel)) return } - channel.Reply(RplPrivMsg(client, channel, m.message)) + channel.Reply(RplPrivMsg(client, channel, message)) } -func (msg *ChannelModeCommand) HandleChannel(channel *Channel) { - client := msg.Client() - - if len(msg.changes) == 0 { +func (channel *Channel) Mode(client *Client, changes ChannelModeChanges) { + if len(changes) == 0 { client.Reply(RplChannelModeIs(channel)) return } - changes := make(ChannelModeChanges, 0) + applied := make(ChannelModeChanges, 0) - for _, change := range msg.changes { + for _, change := range changes { switch change.mode { case BanMask: // TODO add/remove @@ -294,11 +223,11 @@ func (msg *ChannelModeCommand) HandleChannel(channel *Channel) { switch change.op { case Add: channel.flags[change.mode] = true - changes = append(changes, change) + applied = append(applied, change) case Remove: delete(channel.flags, change.mode) - changes = append(changes, change) + applied = append(applied, change) } case Key: @@ -315,11 +244,11 @@ func (msg *ChannelModeCommand) HandleChannel(channel *Channel) { } channel.key = change.arg - changes = append(changes, change) + applied = append(applied, change) case Remove: channel.key = "" - changes = append(changes, change) + applied = append(applied, change) } case ChannelOperator, Voice: @@ -347,40 +276,33 @@ func (msg *ChannelModeCommand) HandleChannel(channel *Channel) { switch change.op { case Add: channel.members[target][change.mode] = true - changes = append(changes, change) + applied = append(applied, change) case Remove: channel.members[target][change.mode] = false - changes = append(changes, change) + applied = append(applied, change) } } } - if len(changes) > 0 { - channel.Reply(RplChannelMode(client, channel, changes)) + if len(applied) > 0 { + channel.Reply(RplChannelMode(client, channel, applied)) } } -func (m *NoticeCommand) HandleChannel(channel *Channel) { - client := m.Client() +func (channel *Channel) Notice(client *Client, message string) { if channel.flags[NoOutside] && !channel.members.Has(client) { client.Reply(ErrCannotSendToChan(channel)) return } - channel.Reply(RplNotice(client, channel, m.message)) + channel.Reply(RplNotice(client, channel, message)) } -func (msg *QuitCommand) HandleChannel(channel *Channel) { - client := msg.Client() - removeClient := &RemoveFriend{ - client: client, - } +func (channel *Channel) Quit(client *Client) { for member := range channel.members { - member.commands <- removeClient + client.RemoveFriend(member) + member.RemoveFriend(client) } + channel.members.Remove(client) } - -func (msg *DestroyClient) HandleChannel(channel *Channel) { - channel.members.Remove(msg.client) -} diff --git a/irc/client.go b/irc/client.go index 6d53a99c..29e9e152 100644 --- a/irc/client.go +++ b/irc/client.go @@ -12,7 +12,6 @@ type Client struct { away bool awayMessage string channels ChannelSet - commands chan ClientCommand ctime time.Time friends map[*Client]uint hostname string @@ -35,7 +34,6 @@ func NewClient(server *Server, conn net.Conn) *Client { client := &Client{ atime: now, channels: make(ChannelSet), - commands: make(chan ClientCommand), ctime: now, friends: make(map[*Client]uint), hostname: AddrLookupHostname(conn.RemoteAddr()), @@ -45,14 +43,42 @@ func NewClient(server *Server, conn net.Conn) *Client { socket: NewSocket(conn), } - client.loginTimer = time.AfterFunc(LOGIN_TIMEOUT, client.Destroy) - go client.readClientCommands() + client.loginTimer = time.AfterFunc(LOGIN_TIMEOUT, client.ConnectionClosed) + go client.readCommands() go client.writeReplies() return client } +func (client *Client) readCommands() { + for line := range client.socket.Read() { + msg, err := ParseCommand(line) + if err != nil { + switch err { + case NotEnoughArgsError: + client.Reply(ErrNeedMoreParams(client.server, line)) + default: + client.Reply(ErrUnknownCommand(client.server, line)) + } + continue + } + + msg.SetClient(client) + client.server.commands <- msg + } +} + +func (client *Client) writeReplies() { + for reply := range client.replies { + if DEBUG_CLIENT { + log.Printf("%s ← %s", client, reply) + } + + client.socket.Write(reply.Format(client)) + } +} + func (client *Client) Touch() { client.atime = time.Now() @@ -82,7 +108,7 @@ func (client *Client) ConnectionTimeout() { message: "connection timeout", } msg.SetClient(client) - client.server.Command(msg) + client.server.commands <- msg } func (client *Client) ConnectionClosed() { @@ -90,57 +116,14 @@ func (client *Client) ConnectionClosed() { message: "connection closed", } msg.SetClient(client) - client.server.Command(msg) -} - -func (client *Client) readClientCommands() { - for command := range client.commands { - command.HandleClient(client) - } -} - -func (c *Client) readCommands() { - for line := range c.socket.Read() { - m, err := ParseCommand(line) - if err != nil { - switch err { - case NotEnoughArgsError: - c.Reply(ErrNeedMoreParams(c.server, line)) - default: - c.Reply(ErrUnknownCommand(c.server, line)) - } - continue - } - - m.SetClient(c) - c.server.Command(m) - } - - if c.phase == Normal { - c.ConnectionClosed() - } else { - c.Destroy() - } -} - -func (client *Client) writeReplies() { - for reply := range client.replies { - if DEBUG_CLIENT { - log.Printf("%s ← %s", client, reply) - } - - if client.socket.Write(reply.Format(client)) != nil { - break - } - } -} - -type DestroyClient struct { - BaseCommand - client *Client + client.server.commands <- msg } func (client *Client) Destroy() { + if DEBUG_CLIENT { + log.Printf("%s destroy", client) + } + client.socket.Close() client.loginTimer.Stop() @@ -153,15 +136,11 @@ func (client *Client) Destroy() { client.quitTimer.Stop() } - cmd := &DestroyClient{ - client: client, - } + client.server.clients.Remove(client) - for channel := range client.channels { - channel.Command(cmd) + if DEBUG_CLIENT { + log.Printf("%s destroyed", client) } - - client.server.Command(cmd) } func (client *Client) Reply(reply Reply) { @@ -220,51 +199,31 @@ func (c *Client) String() string { return c.UserHost() } -// -// commands -// - -type AddFriend struct { - client *Client +func (client *Client) AddFriend(friend *Client) { + client.friends[friend] += 1 } -func (msg *AddFriend) HandleClient(client *Client) { - client.friends[msg.client] += 1 -} - -type RemoveFriend struct { - client *Client -} - -func (msg *RemoveFriend) HandleClient(client *Client) { - client.friends[msg.client] -= 1 - if client.friends[msg.client] <= 0 { - delete(client.friends, msg.client) +func (client *Client) RemoveFriend(friend *Client) { + client.friends[friend] -= 1 + if client.friends[friend] <= 0 { + delete(client.friends, friend) } } -func (msg *JoinChannel) HandleClient(client *Client) { - client.channels.Add(msg.channel) -} - -func (msg *PartChannel) HandleClient(client *Client) { - client.channels.Remove(msg.channel) -} - -func (msg *NickCommand) HandleClient(client *Client) { +func (client *Client) ChangeNickname(nickname string) { // Make reply before changing nick. - reply := RplNick(client, msg.nickname) + reply := RplNick(client, nickname) - client.nick = msg.nickname + client.nick = nickname for friend := range client.friends { friend.Reply(reply) } } -func (msg *QuitCommand) HandleClient(client *Client) { +func (client *Client) Quit(message string) { if len(client.friends) > 0 { - reply := RplQuit(client, msg.message) + reply := RplQuit(client, message) for friend := range client.friends { if friend == client { continue @@ -274,7 +233,7 @@ func (msg *QuitCommand) HandleClient(client *Client) { } for channel := range client.channels { - channel.commands <- msg + channel.Quit(client) } client.Destroy() diff --git a/irc/commands.go b/irc/commands.go index 480ed04f..eeeceda5 100644 --- a/irc/commands.go +++ b/irc/commands.go @@ -362,10 +362,6 @@ func NewPrivMsgCommand(args []string) (editableCommand, error) { }, nil } -func (m *PrivMsgCommand) TargetIsChannel() bool { - return IsChannel(m.target) -} - // TOPIC [newtopic] type TopicCommand struct { diff --git a/irc/server.go b/irc/server.go index 5a12fda4..644e12f4 100644 --- a/irc/server.go +++ b/irc/server.go @@ -83,10 +83,6 @@ func (server *Server) receiveCommands() { } } -func (server *Server) Command(command Command) { - server.commands <- command -} - func (server *Server) InitPhase() Phase { if server.password == "" { return Registration @@ -228,7 +224,8 @@ func (s *Server) Nick() string { // func (msg *ProxyCommand) HandleAuthServer(server *Server) { - msg.Client().hostname = LookupHostname(msg.sourceIP) + client := msg.Client() + client.hostname = LookupHostname(msg.sourceIP) } func (msg *CapCommand) HandleAuthServer(server *Server) { @@ -264,15 +261,15 @@ func (m *NickCommand) HandleRegServer(s *Server) { return } - client.nick = m.nickname + client.ChangeNickname(m.nickname) s.clients.Add(client) s.tryRegister(client) } -func (m *UserMsgCommand) HandleRegServer(s *Server) { - c := m.Client() - c.username, c.realname = m.user, m.realname - s.tryRegister(c) +func (msg *UserMsgCommand) HandleRegServer(server *Server) { + client := msg.Client() + client.username, client.realname = msg.user, msg.realname + server.tryRegister(client) } // @@ -291,54 +288,52 @@ func (m *PongCommand) HandleServer(s *Server) { // no-op } -func (m *NickCommand) HandleServer(s *Server) { - c := m.Client() +func (msg *NickCommand) HandleServer(server *Server) { + client := msg.Client() - if m.nickname == "" { - c.Reply(ErrNoNicknameGiven(s)) + if msg.nickname == "" { + client.Reply(ErrNoNicknameGiven(server)) return } - if s.clients[m.nickname] != nil { - c.Reply(ErrNickNameInUse(s, m.nickname)) + if server.clients[msg.nickname] != nil { + client.Reply(ErrNickNameInUse(server, msg.nickname)) return } - s.clients.Remove(c) - c.commands <- m - s.clients.Add(c) + server.clients.Remove(client) + client.ChangeNickname(msg.nickname) + server.clients.Add(client) } func (m *UserMsgCommand) HandleServer(s *Server) { m.Client().Reply(ErrAlreadyRegistered(s)) } -func (m *QuitCommand) HandleServer(server *Server) { - client := m.Client() - +func (msg *QuitCommand) HandleServer(server *Server) { + client := msg.Client() + client.Quit(msg.message) server.clients.Remove(client) - - client.commands <- m } func (m *JoinCommand) HandleServer(s *Server) { - c := m.Client() + client := m.Client() if m.zero { - cmd := &PartCommand{} - cmd.SetClient(c) - for channel := range c.channels { - channel.Command(cmd) + for channel := range client.channels { + channel.Part(client, client.Nick()) } return } for name := range m.channels { - s.GetOrMakeChannel(name).Command(m) + channel := s.GetOrMakeChannel(name) + channel.Join(client, m.channels[name]) } } func (m *PartCommand) HandleServer(server *Server) { + client := m.Client() for _, chname := range m.channels { channel := server.channels[chname] @@ -347,40 +342,46 @@ func (m *PartCommand) HandleServer(server *Server) { continue } - channel.Command(m) + channel.Part(client, m.Message()) } } -func (m *TopicCommand) HandleServer(s *Server) { - channel := s.channels[m.channel] +func (msg *TopicCommand) HandleServer(server *Server) { + client := msg.Client() + channel := server.channels[msg.channel] if channel == nil { - m.Client().Reply(ErrNoSuchChannel(s, m.channel)) + client.Reply(ErrNoSuchChannel(server, msg.channel)) return } - channel.Command(m) + if msg.setTopic { + channel.SetTopic(client, msg.topic) + } else { + channel.GetTopic(client) + } } -func (m *PrivMsgCommand) HandleServer(s *Server) { - if m.TargetIsChannel() { - channel := s.channels[m.target] +func (msg *PrivMsgCommand) HandleServer(server *Server) { + client := msg.Client() + if IsChannel(msg.target) { + channel := server.channels[msg.target] if channel == nil { - m.Client().Reply(ErrNoSuchChannel(s, m.target)) + client.Reply(ErrNoSuchChannel(server, msg.target)) return } - channel.Command(m) + channel.PrivMsg(client, msg.message) return } - target := s.clients[m.target] + target := server.clients[msg.target] if target == nil { - m.Client().Reply(ErrNoSuchNick(s, m.target)) + client.Reply(ErrNoSuchNick(server, msg.target)) return } - target.Reply(RplPrivMsg(m.Client(), target, m.message)) + target.Reply(RplPrivMsg(client, target, msg.message)) if target.away { - m.Client().Reply(RplAway(s, target)) + client.Reply(RplAway(server, target)) } } @@ -436,7 +437,8 @@ func (msg *ChannelModeCommand) HandleServer(server *Server) { client.Reply(ErrNoSuchChannel(server, msg.channel)) return } - channel.Command(msg) + + channel.Mode(client, msg.changes) } func whoChannel(client *Client, server *Server, channel *Channel) { @@ -513,29 +515,22 @@ func (msg *MOTDCommand) HandleServer(server *Server) { } func (msg *NoticeCommand) HandleServer(server *Server) { + client := msg.Client() if IsChannel(msg.target) { channel := server.channels[msg.target] if channel == nil { - msg.Client().Reply(ErrNoSuchChannel(server, msg.target)) + client.Reply(ErrNoSuchChannel(server, msg.target)) return } - channel.Command(msg) + channel.Notice(client, msg.message) return } target := server.clients[msg.target] if target == nil { - msg.Client().Reply(ErrNoSuchNick(server, msg.target)) + client.Reply(ErrNoSuchNick(server, msg.target)) return } - target.Reply(RplPrivMsg(msg.Client(), target, msg.message)) -} - -func (msg *DestroyChannel) HandleServer(server *Server) { - server.channels.Remove(msg.channel) -} - -func (msg *DestroyClient) HandleServer(server *Server) { - server.clients.Remove(msg.client) + target.Reply(RplNotice(client, target, msg.message)) } diff --git a/irc/socket.go b/irc/socket.go index 7801ee75..03f09a03 100644 --- a/irc/socket.go +++ b/irc/socket.go @@ -2,17 +2,13 @@ package irc import ( "bufio" - "io" "log" "net" "strings" - "sync" ) type Socket struct { - closed bool conn net.Conn - mutex *sync.Mutex reader *bufio.Reader receive chan string send chan string @@ -25,7 +21,6 @@ func NewSocket(conn net.Conn) *Socket { reader: bufio.NewReader(conn), receive: make(chan string), send: make(chan string), - mutex: &sync.Mutex{}, writer: bufio.NewWriter(conn), } @@ -39,40 +34,19 @@ func (socket *Socket) String() string { return socket.conn.RemoteAddr().String() } -func (socket *Socket) IsClosed() bool { - socket.mutex.Lock() - defer socket.mutex.Unlock() - return socket.closed -} - func (socket *Socket) Close() { - if socket.IsClosed() { - return - } - - if DEBUG_NET { - log.Printf("%s closed", socket) - } - - socket.mutex.Lock() - socket.closed = true socket.conn.Close() - close(socket.receive) - socket.mutex.Unlock() } func (socket *Socket) Read() <-chan string { return socket.receive } -func (socket *Socket) Write(lines []string) error { +func (socket *Socket) Write(lines []string) { for _, line := range lines { - if socket.IsClosed() { - return io.EOF - } socket.send <- line } - return nil + return } func (socket *Socket) readLines() { @@ -92,7 +66,7 @@ func (socket *Socket) readLines() { socket.receive <- line } - socket.Close() + close(socket.receive) } func (socket *Socket) writeLines() { @@ -106,6 +80,7 @@ func (socket *Socket) writeLines() { if _, err := socket.writer.WriteString(CRLF); socket.maybeLogWriteError(err) { break } + if err := socket.writer.Flush(); socket.maybeLogWriteError(err) { break } diff --git a/irc/types.go b/irc/types.go index 9ce6b236..b3896d8b 100644 --- a/irc/types.go +++ b/irc/types.go @@ -192,15 +192,6 @@ type RegServerCommand interface { HandleRegServer(*Server) } -type ChannelCommand interface { - Command - HandleChannel(channel *Channel) -} - -type ClientCommand interface { - HandleClient(client *Client) -} - // // structs //