diff --git a/irc/channel.go b/irc/channel.go index d985d6f7..07b52913 100644 --- a/irc/channel.go +++ b/irc/channel.go @@ -444,8 +444,8 @@ func (channel *Channel) Persist() (err error) { (name, flags, key, topic, user_limit, ban_list, except_list, invite_list) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, - channel.name, channel.flags.String(), channel.key, channel.topic, - channel.userLimit, channel.lists[BanMask].String(), + channel.name.String(), channel.flags.String(), channel.key.String(), + channel.topic.String(), channel.userLimit, channel.lists[BanMask].String(), channel.lists[ExceptMask].String(), channel.lists[InviteMask].String()) } else { _, err = channel.server.db.Exec(` diff --git a/irc/client_lookup_set.go b/irc/client_lookup_set.go index ea53a9e9..844e3f3e 100644 --- a/irc/client_lookup_set.go +++ b/irc/client_lookup_set.go @@ -153,7 +153,7 @@ func NewClientDB() *ClientDB { func (db *ClientDB) Add(client *Client) { _, err := db.db.Exec(`INSERT INTO client (nickname, userhost) VALUES (?, ?)`, - client.Nick(), client.UserHost()) + client.Nick().String(), client.UserHost().String()) if err != nil { Log.error.Println("ClientDB.Add:", err) } @@ -161,7 +161,7 @@ func (db *ClientDB) Add(client *Client) { func (db *ClientDB) Remove(client *Client) { _, err := db.db.Exec(`DELETE FROM client WHERE nickname = ?`, - client.Nick()) + client.Nick().String()) if err != nil { Log.error.Println("ClientDB.Remove:", err) } diff --git a/irc/constants.go b/irc/constants.go index 1210c76d..d9426cb8 100644 --- a/irc/constants.go +++ b/irc/constants.go @@ -1,14 +1,5 @@ package irc -import ( - "errors" -) - -var ( - // errors - ErrAlreadyDestroyed = errors.New("already destroyed") -) - const ( SEM_VER = "ergonomadic-1.3.1" CRLF = "\r\n" @@ -184,35 +175,4 @@ const ( ERR_NOOPERHOST NumericCode = 491 ERR_UMODEUNKNOWNFLAG NumericCode = 501 ERR_USERSDONTMATCH NumericCode = 502 - - Add ModeOp = '+' - List ModeOp = '=' - Remove ModeOp = '-' - - Away UserMode = 'a' - Invisible UserMode = 'i' - LocalOperator UserMode = 'O' - Operator UserMode = 'o' - Restricted UserMode = 'r' - ServerNotice UserMode = 's' // deprecated - WallOps UserMode = 'w' - - Anonymous ChannelMode = 'a' // flag - BanMask ChannelMode = 'b' // arg - ChannelCreator ChannelMode = 'O' // flag - ChannelOperator ChannelMode = 'o' // arg - ExceptMask ChannelMode = 'e' // arg - InviteMask ChannelMode = 'I' // arg - InviteOnly ChannelMode = 'i' // flag - Key ChannelMode = 'k' // flag arg - Moderated ChannelMode = 'm' // flag - NoOutside ChannelMode = 'n' // flag - OpOnlyTopic ChannelMode = 't' // flag - Persistent ChannelMode = 'P' // flag - Private ChannelMode = 'p' // flag - Quiet ChannelMode = 'q' // flag - ReOp ChannelMode = 'r' // flag - Secret ChannelMode = 's' // flag, deprecated - UserLimit ChannelMode = 'l' // flag arg - Voice ChannelMode = 'v' // arg ) diff --git a/irc/modes.go b/irc/modes.go new file mode 100644 index 00000000..7dfb3c08 --- /dev/null +++ b/irc/modes.go @@ -0,0 +1,162 @@ +package irc + +import ( + "strings" +) + +// user mode flags +type UserMode rune + +func (mode UserMode) String() string { + return string(mode) +} + +type UserModes []UserMode + +func (modes UserModes) String() string { + strs := make([]string, len(modes)) + for index, mode := range modes { + strs[index] = mode.String() + } + return strings.Join(strs, "") +} + +// channel mode flags +type ChannelMode rune + +func (mode ChannelMode) String() string { + return string(mode) +} + +type ChannelModes []ChannelMode + +func (modes ChannelModes) String() string { + strs := make([]string, len(modes)) + for index, mode := range modes { + strs[index] = mode.String() + } + return strings.Join(strs, "") +} + +type ModeOp rune + +func (op ModeOp) String() string { + return string(op) +} + +const ( + Add ModeOp = '+' + List ModeOp = '=' + Remove ModeOp = '-' +) + +const ( + Away UserMode = 'a' + Invisible UserMode = 'i' + LocalOperator UserMode = 'O' + Operator UserMode = 'o' + Restricted UserMode = 'r' + ServerNotice UserMode = 's' // deprecated + WallOps UserMode = 'w' +) + +var ( + SupportedUserModes = UserModes{ + Away, Invisible, Operator, + } +) + +const ( + Anonymous ChannelMode = 'a' // flag + BanMask ChannelMode = 'b' // arg + ChannelCreator ChannelMode = 'O' // flag + ChannelOperator ChannelMode = 'o' // arg + ExceptMask ChannelMode = 'e' // arg + InviteMask ChannelMode = 'I' // arg + InviteOnly ChannelMode = 'i' // flag + Key ChannelMode = 'k' // flag arg + Moderated ChannelMode = 'm' // flag + NoOutside ChannelMode = 'n' // flag + OpOnlyTopic ChannelMode = 't' // flag + Persistent ChannelMode = 'P' // flag + Private ChannelMode = 'p' // flag + Quiet ChannelMode = 'q' // flag + ReOp ChannelMode = 'r' // flag + Secret ChannelMode = 's' // flag, deprecated + UserLimit ChannelMode = 'l' // flag arg + Voice ChannelMode = 'v' // arg +) + +var ( + SupportedChannelModes = ChannelModes{ + BanMask, ExceptMask, InviteMask, InviteOnly, Key, NoOutside, + OpOnlyTopic, Persistent, Private, UserLimit, + } +) + +// +// commands +// + +func (m *ModeCommand) HandleServer(s *Server) { + client := m.Client() + target := s.clients.Get(m.nickname) + + if target == nil { + client.ErrNoSuchNick(m.nickname) + return + } + + if client != target && !client.flags[Operator] { + client.ErrUsersDontMatch() + return + } + + changes := make(ModeChanges, 0, len(m.changes)) + + for _, change := range m.changes { + switch change.mode { + case Invisible, ServerNotice, WallOps: + switch change.op { + case Add: + if target.flags[change.mode] { + continue + } + target.flags[change.mode] = true + changes = append(changes, change) + + case Remove: + if !target.flags[change.mode] { + continue + } + delete(target.flags, change.mode) + changes = append(changes, change) + } + + case Operator, LocalOperator: + if change.op == Remove { + if !target.flags[change.mode] { + continue + } + delete(target.flags, change.mode) + changes = append(changes, change) + } + } + } + + // Who should get these replies? + if len(changes) > 0 { + client.Reply(RplMode(client, target, changes)) + } +} + +func (msg *ChannelModeCommand) HandleServer(server *Server) { + client := msg.Client() + channel := server.channels.Get(msg.channel) + if channel == nil { + client.ErrNoSuchChannel(msg.channel) + return + } + + channel.Mode(client, msg.changes) +} diff --git a/irc/reply.go b/irc/reply.go index a421e78b..695ac9e5 100644 --- a/irc/reply.go +++ b/irc/reply.go @@ -181,7 +181,8 @@ func (target *Client) RplCreated() { func (target *Client) RplMyInfo() { target.NumericReply(RPL_MYINFO, - "%s %s aiOorsw abeIikmntpqrsl", target.server.name, SEM_VER) + "%s %s %s %s", + target.server.name, SEM_VER, SupportedUserModes, SupportedChannelModes) } func (target *Client) RplUModeIs(client *Client) { diff --git a/irc/server.go b/irc/server.go index 002d88cc..b332b882 100644 --- a/irc/server.go +++ b/irc/server.go @@ -94,9 +94,7 @@ func (server *Server) loadChannels() { log.Fatal("error loading channels: ", err) } for rows.Next() { - var name Name - var flags string - var key, topic Text + var name, flags, key, topic string var userLimit uint64 var banList, exceptList, inviteList string err = rows.Scan(&name, &flags, &key, &topic, &userLimit, &banList, @@ -106,12 +104,12 @@ func (server *Server) loadChannels() { continue } - channel := NewChannel(server, name) + channel := NewChannel(server, NewName(name)) for _, flag := range flags { channel.flags[ChannelMode(flag)] = true } - channel.key = key - channel.topic = topic + channel.key = NewText(key) + channel.topic = NewText(topic) channel.userLimit = userLimit loadChannelList(channel, banList, BanMask) loadChannelList(channel, exceptList, ExceptMask) @@ -339,7 +337,7 @@ func (msg *RFC2812UserCommand) HandleRegServer(server *Server) { } flags := msg.Flags() if len(flags) > 0 { - for _, mode := range msg.Flags() { + for _, mode := range flags { client.flags[mode] = true } client.RplUModeIs(client) @@ -491,58 +489,6 @@ func (msg *PrivMsgCommand) HandleServer(server *Server) { } } -func (m *ModeCommand) HandleServer(s *Server) { - client := m.Client() - target := s.clients.Get(m.nickname) - - if target == nil { - client.ErrNoSuchNick(m.nickname) - return - } - - if client != target && !client.flags[Operator] { - client.ErrUsersDontMatch() - return - } - - changes := make(ModeChanges, 0, len(m.changes)) - - for _, change := range m.changes { - switch change.mode { - case Invisible, ServerNotice, WallOps: - switch change.op { - case Add: - if target.flags[change.mode] { - continue - } - target.flags[change.mode] = true - changes = append(changes, change) - - case Remove: - if !target.flags[change.mode] { - continue - } - delete(target.flags, change.mode) - changes = append(changes, change) - } - - case Operator, LocalOperator: - if change.op == Remove { - if !target.flags[change.mode] { - continue - } - delete(target.flags, change.mode) - changes = append(changes, change) - } - } - } - - // Who should get these replies? - if len(changes) > 0 { - client.Reply(RplMode(client, target, changes)) - } -} - func (client *Client) WhoisChannelsNames() []string { chstrs := make([]string, len(client.channels)) index := 0 @@ -579,17 +525,6 @@ func (m *WhoisCommand) HandleServer(server *Server) { } } -func (msg *ChannelModeCommand) HandleServer(server *Server) { - client := msg.Client() - channel := server.channels.Get(msg.channel) - if channel == nil { - client.ErrNoSuchChannel(msg.channel) - return - } - - channel.Mode(client, msg.changes) -} - func whoChannel(client *Client, channel *Channel, friends ClientSet) { for member := range channel.members { if !client.flags[Invisible] || friends[client] { diff --git a/irc/types.go b/irc/types.go index a1d02603..7c8a5a2f 100644 --- a/irc/types.go +++ b/irc/types.go @@ -9,27 +9,6 @@ import ( // simple types // -// add, remove, list modes -type ModeOp rune - -func (op ModeOp) String() string { - return string(op) -} - -// user mode flags -type UserMode rune - -func (mode UserMode) String() string { - return string(mode) -} - -// channel mode flags -type ChannelMode rune - -func (mode ChannelMode) String() string { - return string(mode) -} - type ChannelNameMap map[Name]*Channel func (channels ChannelNameMap) Get(name Name) *Channel {