From f04dd7c5d57bf6eb57ebf4c58687ce14d5e2f88a Mon Sep 17 00:00:00 2001 From: Jeremy Latt Date: Tue, 4 Feb 2014 19:28:24 -0800 Subject: [PATCH] get rid of user abstraction, services --- build.sh | 6 +- src/irc/channel.go | 134 ++++++++------------ src/irc/client.go | 59 ++++++--- src/irc/commands.go | 5 - src/irc/net.go | 4 +- src/irc/nickserv.go | 158 ------------------------ src/irc/persistence.go | 270 ----------------------------------------- src/irc/reply.go | 9 +- src/irc/server.go | 172 +++++++------------------- src/irc/service.go | 83 ------------- src/irc/user.go | 228 ---------------------------------- 11 files changed, 145 insertions(+), 983 deletions(-) delete mode 100644 src/irc/nickserv.go delete mode 100644 src/irc/persistence.go delete mode 100644 src/irc/service.go delete mode 100644 src/irc/user.go diff --git a/build.sh b/build.sh index f5ed68c3..9cbb1205 100755 --- a/build.sh +++ b/build.sh @@ -1,5 +1,5 @@ #!/bin/bash export GOPATH="$PWD" -go get "code.google.com/p/go.crypto/bcrypt" -go get "github.com/mattn/go-sqlite3" -go install ergonomadic genpasswd ergonomadicdb +#go get "code.google.com/p/go.crypto/bcrypt" +#go get "github.com/mattn/go-sqlite3" +go install ergonomadic genpasswd diff --git a/src/irc/channel.go b/src/irc/channel.go index ccb7e121..ad824d18 100644 --- a/src/irc/channel.go +++ b/src/irc/channel.go @@ -9,82 +9,41 @@ const ( ) type Channel struct { - id *RowId - server *Server commands chan<- ChannelCommand - replies chan<- Reply - name string key string - topic string - members UserSet + members ClientSet + name string noOutside bool password string + replies chan<- Reply + server *Server + topic string } type ChannelSet map[*Channel]bool -func (set ChannelSet) Add(channel *Channel) { - set[channel] = true -} - -func (set ChannelSet) Remove(channel *Channel) { - delete(set, channel) -} - -func (set ChannelSet) Ids() (ids []RowId) { - ids = make([]RowId, len(set)) - var i = 0 - for channel := range set { - ids[i] = *(channel.id) - i++ - } - return ids -} - type ChannelCommand interface { Command HandleChannel(channel *Channel) } -// NewChannel creates a new channel from a `Server` and a `name` string, which -// must be unique on the server. +// 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{ name: name, - members: make(UserSet), + members: make(ClientSet), server: s, commands: commands, replies: replies, } go channel.receiveCommands(commands) go channel.receiveReplies(replies) - Save(s.db, channel) return channel } -func (channel *Channel) Save(q Queryable) bool { - if channel.id == nil { - if err := InsertChannel(q, channel); err != nil { - log.Println(err) - return false - } - channelId, err := FindChannelIdByName(q, channel.name) - if err != nil { - log.Println(err) - return false - } - channel.id = &channelId - } else { - if err := UpdateChannel(q, channel); err != nil { - log.Println(err) - return false - } - } - return true -} - func (channel *Channel) receiveCommands(commands <-chan ChannelCommand) { for command := range commands { if DEBUG_CHANNEL { @@ -99,16 +58,13 @@ func (channel *Channel) receiveReplies(replies <-chan Reply) { if DEBUG_CHANNEL { log.Printf("%s ← %s : %s", channel, reply.Source(), reply) } - for user := range channel.members { - if user != reply.Source() { - user.Replies() <- reply + for client := range channel.members { + if client != reply.Source() { + client.replies <- reply } } } } -func (channel *Channel) Nicks() []string { - return channel.members.Nicks() -} func (channel *Channel) IsEmpty() bool { return len(channel.members) == 0 @@ -127,6 +83,16 @@ func (channel *Channel) GetUsers(replier Replier) { replier.Replies() <- NewNamesReply(channel) } +func (channel *Channel) Nicks() []string { + nicks := make([]string, len(channel.members)) + i := 0 + for client := range channel.members { + nicks[i] = client.Nick() + i += 1 + } + return nicks +} + func (channel *Channel) Replies() chan<- Reply { return channel.replies } @@ -143,20 +109,22 @@ func (channel *Channel) PublicId() string { return channel.name } -func (channel *Channel) Commands() chan<- ChannelCommand { - return channel.commands -} - func (channel *Channel) String() string { return channel.Id() } -func (channel *Channel) Join(user *User) { - channel.members.Add(user) - user.channels.Add(channel) - channel.Replies() <- RplJoin(channel, user) - channel.GetTopic(user) - channel.GetUsers(user) +func (channel *Channel) Join(client *Client) { + channel.members[client] = true + client.channels[channel] = true + reply := RplJoin(channel, client) + client.replies <- reply + channel.replies <- reply + channel.GetTopic(client) + channel.GetUsers(client) +} + +func (channel *Channel) HasMember(client *Client) bool { + return channel.members[client] } // @@ -166,58 +134,54 @@ func (channel *Channel) Join(user *User) { func (m *JoinCommand) HandleChannel(channel *Channel) { client := m.Client() if channel.key != m.channels[channel.name] { - client.Replies() <- ErrBadChannelKey(channel) + client.replies <- ErrBadChannelKey(channel) return } - channel.Join(client.user) + channel.Join(client) } func (m *PartCommand) HandleChannel(channel *Channel) { - user := m.Client().user + c := m.Client() - if !channel.members[user] { - user.Replies() <- ErrNotOnChannel(channel) + if !channel.HasMember(c) { + c.replies <- ErrNotOnChannel(channel) return } msg := m.message if msg == "" { - msg = user.Nick() + msg = c.Nick() } - channel.Replies() <- RplPart(channel, user, msg) + channel.replies <- RplPart(channel, c, msg) - channel.members.Remove(user) - user.channels.Remove(channel) + delete(channel.members, c) + delete(c.channels, channel) - if channel.IsEmpty() { + if channel.IsEmpty() { // TODO persistent channels channel.server.DeleteChannel(channel) } } func (m *TopicCommand) HandleChannel(channel *Channel) { - user := m.User() + client := m.Client() - if !channel.members[user] { - user.Replies() <- ErrNotOnChannel(channel) + if !channel.HasMember(client) { + client.replies <- ErrNotOnChannel(channel) return } if m.topic == "" { - channel.GetTopic(user) + channel.GetTopic(client) return } channel.topic = m.topic - if channel.topic == "" { - channel.Replies() <- RplNoTopic(channel) - return - } - channel.Replies() <- RplTopic(channel) + channel.GetTopic(channel) } func (m *PrivMsgCommand) HandleChannel(channel *Channel) { - channel.Replies() <- RplPrivMsgChannel(channel, m.User(), m.message) + channel.replies <- RplPrivMsgChannel(channel, m.Client(), m.message) } diff --git a/src/irc/client.go b/src/irc/client.go index a0652e80..6e938456 100644 --- a/src/irc/client.go +++ b/src/irc/client.go @@ -8,22 +8,28 @@ import ( ) const ( - DEBUG_CLIENT = true + DEBUG_CLIENT = false ) +type ClientCommand interface { + Command + HandleClient(*Client) +} + type Client struct { + atime time.Time + away bool + channels ChannelSet + commands chan<- ClientCommand conn net.Conn - username string - realname string hostname string nick string - serverPass bool + realname string registered bool - away bool - server *Server - atime time.Time - user *User replies chan<- Reply + server *Server + serverPass bool + username string } type ClientSet map[*Client]bool @@ -31,17 +37,21 @@ type ClientSet map[*Client]bool func NewClient(server *Server, conn net.Conn) *Client { read := StringReadChan(conn) write := StringWriteChan(conn) + commands := make(chan ClientCommand) replies := make(chan Reply) client := &Client{ + channels: make(ChannelSet), + commands: commands, conn: conn, - hostname: conn.RemoteAddr().String(), - server: server, + hostname: LookupHostname(conn.RemoteAddr()), replies: replies, + server: server, } go client.readConn(read) go client.writeConn(write, replies) + go client.receiveCommands(commands) return client } @@ -51,9 +61,9 @@ func (c *Client) readConn(recv <-chan string) { m, err := ParseCommand(str) if err != nil { if err == NotEnoughArgsError { - c.Replies() <- ErrNeedMoreParams(c.server, str) + c.replies <- ErrNeedMoreParams(c.server, str) } else { - c.Replies() <- ErrUnknownCommand(c.server, str) + c.replies <- ErrUnknownCommand(c.server, str) } continue } @@ -72,6 +82,15 @@ func (c *Client) writeConn(write chan<- string, replies <-chan Reply) { } } +func (client *Client) receiveCommands(commands <-chan ClientCommand) { + for command := range commands { + if DEBUG_CLIENT { + log.Printf("%s → %s : %s", command.Client(), client, command) + } + command.HandleClient(client) + } +} + func (c *Client) Replies() chan<- Reply { return c.replies } @@ -81,11 +100,7 @@ func (c *Client) Server() *Server { } func (c *Client) Nick() string { - if c.user != nil { - return c.user.Nick() - } - - if c.nick != "" { + if c.HasNick() { return c.nick } @@ -97,7 +112,7 @@ func (c *Client) UModeString() string { } func (c *Client) HasNick() bool { - return c.Nick() != "" + return c.nick != "" } func (c *Client) HasUsername() bool { @@ -126,3 +141,11 @@ func (c *Client) String() string { func (c *Client) PublicId() string { return fmt.Sprintf("%s!%s@%s", c.Nick(), c.Nick(), c.server.Id()) } + +// +// commands +// + +func (m *PrivMsgCommand) HandleClient(client *Client) { + client.replies <- RplPrivMsg(m.Client(), client, m.message) +} diff --git a/src/irc/commands.go b/src/irc/commands.go index 1ecb5090..53b529a7 100644 --- a/src/irc/commands.go +++ b/src/irc/commands.go @@ -9,7 +9,6 @@ import ( type Command interface { Client() *Client - User() *User Source() Identifier Reply(Reply) HandleServer(*Server) @@ -46,10 +45,6 @@ func (command *BaseCommand) Client() *Client { return command.client } -func (command *BaseCommand) User() *User { - return command.Client().user -} - func (command *BaseCommand) SetBase(c *Client) { *command = BaseCommand{c} } diff --git a/src/irc/net.go b/src/irc/net.go index d241a0d0..043fc658 100644 --- a/src/irc/net.go +++ b/src/irc/net.go @@ -8,7 +8,7 @@ import ( ) const ( - DEBUG_NET = true + DEBUG_NET = false ) func readTrimmedLine(reader *bufio.Reader) (string, error) { @@ -64,7 +64,7 @@ func LookupHostname(addr net.Addr) string { if err != nil { return addrStr } - names, err := net.LookupAddr(ipaddr) + names, err := net.LookupHost(ipaddr) if err != nil { return ipaddr } diff --git a/src/irc/nickserv.go b/src/irc/nickserv.go deleted file mode 100644 index e0fd5880..00000000 --- a/src/irc/nickserv.go +++ /dev/null @@ -1,158 +0,0 @@ -package irc - -import ( - "fmt" - "log" -) - -const ( - DEBUG_NICKSERV = true -) - -type NickServCommand interface { - HandleNickServ(*NickServ) - Client() *Client - SetBase(*Client) -} - -type NickServ struct { - BaseService -} - -func NewNickServ(s *Server) Service { - return NewService(new(NickServ), s, "NickServ") -} - -func (ns *NickServ) SetBase(base *BaseService) { - ns.BaseService = *base -} - -func (ns *NickServ) Debug() bool { - return DEBUG_NICKSERV -} - -var ( - parseNickServCommandFuncs = map[string]func([]string) (NickServCommand, error){ - "REGISTER": NewRegisterCommand, - "IDENTIFY": NewIdentifyCommand, - } -) - -// -// commands -// - -func (ns *NickServ) HandlePrivMsg(m *PrivMsgCommand) { - command, args := parseLine(m.message) - constructor := parseNickServCommandFuncs[command] - if constructor == nil { - ns.Reply(m.Client(), "Unknown command.") - return - } - - cmd, err := constructor(args) - if err != nil { - ns.Reply(m.Client(), "Not enough parameters.") - return - } - - cmd.SetBase(m.Client()) - if ns.Debug() { - log.Printf("%s ← %s %s", ns, cmd.Client(), cmd) - } - - cmd.HandleNickServ(ns) -} - -// -// sub-commands -// - -type RegisterCommand struct { - BaseCommand - password string - email string -} - -func (m *RegisterCommand) String() string { - return fmt.Sprintf("REGISTER(email=%s, password=%s)", m.email, m.password) -} - -func NewRegisterCommand(args []string) (NickServCommand, error) { - if len(args) == 0 { - return nil, NotEnoughArgsError - } - - cmd := &RegisterCommand{ - BaseCommand: BaseCommand{}, - password: args[0], - } - if len(args) > 1 { - cmd.email = args[1] - } - return cmd, nil -} - -func (m *RegisterCommand) HandleNickServ(ns *NickServ) { - client := m.Client() - - if client.user != nil { - ns.Reply(client, "You are already registered.") - return - } - - if ns.server.users[client.nick] != nil { - ns.Reply(client, "That nick is already registered.") - return - } - - user := NewUser(client.nick, ns.server) - user.SetPassword(m.password) - Save(ns.server.db, user) - ns.Reply(client, "You have registered.") - - if !user.Login(client, client.nick, m.password) { - ns.Reply(client, "Login failed.") - return - } - ns.Reply(client, "Logged in.") -} - -type IdentifyCommand struct { - BaseCommand - password string -} - -func (m *IdentifyCommand) String() string { - return fmt.Sprintf("IDENTIFY(password=%s)", m.password) -} - -func NewIdentifyCommand(args []string) (NickServCommand, error) { - if len(args) == 0 { - return nil, NotEnoughArgsError - } - - return &IdentifyCommand{ - BaseCommand: BaseCommand{}, - password: args[0], - }, nil -} - -func (m *IdentifyCommand) HandleNickServ(ns *NickServ) { - client := m.Client() - if client.user != nil { - ns.Reply(client, "That nick is already registered.") - return - } - - user := ns.server.users[client.nick] - if user == nil { - ns.Reply(client, "No such nick.") - return - } - - if !user.Login(client, client.nick, m.password) { - ns.Reply(client, "Login failed.") - } - ns.Reply(client, "Logged in.") -} diff --git a/src/irc/persistence.go b/src/irc/persistence.go deleted file mode 100644 index 8a450855..00000000 --- a/src/irc/persistence.go +++ /dev/null @@ -1,270 +0,0 @@ -package irc - -import ( - "database/sql" - //"fmt" - "bufio" - _ "github.com/mattn/go-sqlite3" - "log" - "os" - "path/filepath" - "strings" -) - -type RowId uint64 - -type Queryable interface { - Exec(string, ...interface{}) (sql.Result, error) - Query(string, ...interface{}) (*sql.Rows, error) - QueryRow(string, ...interface{}) *sql.Row -} - -type Savable interface { - Save(q Queryable) bool -} - -type Loadable interface { - Load(q Queryable) bool -} - -// -// general -// - -func NewDatabase() (db *sql.DB) { - db, err := sql.Open("sqlite3", "ergonomadic.db") - if err != nil { - log.Fatalln("cannot open database") - } - return -} - -func readLines(filename string) <-chan string { - file, err := os.Open(filename) - if err != nil { - log.Fatalln(err) - } - reader := bufio.NewReader(file) - lines := make(chan string) - go func(lines chan<- string) { - defer file.Close() - defer close(lines) - for { - line, err := reader.ReadString(';') - if err != nil { - break - } - line = strings.TrimSpace(line) - if line == "" { - continue - } - lines <- line - } - }(lines) - return lines -} - -func ExecSqlFile(db *sql.DB, filename string) { - Transact(db, func(q Queryable) bool { - for line := range readLines(filepath.Join("sql", filename)) { - log.Println(line) - _, err := q.Exec(line) - if err != nil { - log.Fatalln(err) - } - } - return true - }) -} - -func Transact(db *sql.DB, txf func(Queryable) bool) { - tx, err := db.Begin() - if err != nil { - log.Panicln(err) - } - if txf(tx) { - tx.Commit() - } else { - tx.Rollback() - } -} - -func Save(db *sql.DB, s Savable) { - Transact(db, s.Save) -} - -func Load(db *sql.DB, l Loadable) { - Transact(db, l.Load) -} - -// -// general purpose sql -// - -func findId(q Queryable, sql string, args ...interface{}) (rowId RowId, err error) { - row := q.QueryRow(sql, args...) - err = row.Scan(&rowId) - return -} - -func countRows(q Queryable, sql string, args ...interface{}) (count uint, err error) { - row := q.QueryRow(sql, args...) - err = row.Scan(&count) - return -} - -// -// data -// - -type UserRow struct { - id RowId - nick string - hash []byte -} - -type ChannelRow struct { - id RowId - name string -} - -// user - -func FindAllUsers(q Queryable) (urs []*UserRow, err error) { - var rows *sql.Rows - rows, err = q.Query("SELECT id, nick, hash FROM user") - if err != nil { - return - } - urs = make([]*UserRow, 0) - for rows.Next() { - ur := &UserRow{} - err = rows.Scan(&(ur.id), &(ur.nick), &(ur.hash)) - if err != nil { - return - } - urs = append(urs, ur) - } - return -} - -func FindUserByNick(q Queryable, nick string) (ur *UserRow, err error) { - ur = &UserRow{} - row := q.QueryRow("SELECT id, nick, hash FROM user LIMIT 1 WHERE nick = ?", - nick) - err = row.Scan(&(ur.id), &(ur.nick), &(ur.hash)) - return -} - -func FindUserIdByNick(q Queryable, nick string) (RowId, error) { - return findId(q, "SELECT id FROM user WHERE nick = ?", nick) -} - -func FindChannelByName(q Queryable, name string) (cr *ChannelRow) { - cr = new(ChannelRow) - row := q.QueryRow("SELECT id, name FROM channel LIMIT 1 WHERE name = ?", name) - err := row.Scan(&(cr.id), &(cr.name)) - if err != nil { - cr = nil - } - return -} - -func InsertUser(q Queryable, row *UserRow) (err error) { - _, err = q.Exec("INSERT INTO user (nick, hash) VALUES (?, ?)", - row.nick, row.hash) - return -} - -func UpdateUser(q Queryable, row *UserRow) (err error) { - _, err = q.Exec("UPDATE user SET nick = ?, hash = ? WHERE id = ?", - row.nick, row.hash, row.id) - return -} - -func DeleteUser(q Queryable, id RowId) (err error) { - _, err = q.Exec("DELETE FROM user WHERE id = ?", id) - return -} - -// user-channel - -func DeleteAllUserChannels(q Queryable, rowId RowId) (err error) { - _, err = q.Exec("DELETE FROM user_channel WHERE user_id = ?", rowId) - return -} - -func DeleteOtherUserChannels(q Queryable, userId RowId, channelIds []RowId) (err error) { - _, err = q.Exec(`DELETE FROM user_channel WHERE -user_id = ? AND channel_id NOT IN ?`, userId, channelIds) - return -} - -func InsertUserChannels(q Queryable, userId RowId, channelIds []RowId) (err error) { - ins := "INSERT OR IGNORE INTO user_channel (user_id, channel_id) VALUES " - vals := strings.Repeat("(?, ?), ", len(channelIds)) - vals = vals[0 : len(vals)-2] - args := make([]RowId, 2*len(channelIds)) - for i, channelId := range channelIds { - args[i] = userId - args[i+1] = channelId - } - _, err = q.Exec(ins+vals, args) - return -} - -// channel - -func FindChannelIdByName(q Queryable, name string) (RowId, error) { - return findId(q, "SELECT id FROM channel WHERE name = ?", name) -} - -func findChannels(q Queryable, where string, args ...interface{}) (crs []*ChannelRow, err error) { - count, err := countRows(q, "SELECT COUNT(id) FROM channel "+where, args...) - if err != nil { - return - } - rows, err := q.Query("SELECT id, name FROM channel "+where, args...) - if err != nil { - return - } - crs = make([]*ChannelRow, count) - var i = 0 - for rows.Next() { - cr := &ChannelRow{} - err = rows.Scan(&(cr.id), &(cr.name)) - if err != nil { - return - } - crs[i] = cr - i++ - } - return -} - -func FindChannelsForUser(q Queryable, userId RowId) (crs []*ChannelRow, err error) { - crs, err = findChannels(q, - "WHERE id IN (SELECT channel_id from user_channel WHERE user_id = ?)", userId) - return -} - -func FindAllChannels(q Queryable) (crs []*ChannelRow, err error) { - crs, err = findChannels(q, "") - return -} - -func InsertChannel(q Queryable, channel *Channel) (err error) { - _, err = q.Exec("INSERT INTO channel (name) VALUES (?)", channel.name) - return -} - -func UpdateChannel(q Queryable, channel *Channel) (err error) { - _, err = q.Exec("UPDATE channel SET name = ? WHERE id = ?", - channel.name, *(channel.id)) - return -} - -func DeleteChannel(q Queryable, channel *Channel) (err error) { - _, err = q.Exec("DELETE FROM channel WHERE id = ?", *(channel.id)) - return -} diff --git a/src/irc/reply.go b/src/irc/reply.go index 918ed4f8..036ba5d8 100644 --- a/src/irc/reply.go +++ b/src/irc/reply.go @@ -93,6 +93,7 @@ func NewNamesReply(channel *Channel) Reply { BaseReply: &BaseReply{ source: channel, }, + channel: channel, } } @@ -142,12 +143,12 @@ func RplPrivMsgChannel(channel *Channel, source Identifier, message string) Repl return NewStringReply(source, RPL_PRIVMSG, "%s :%s", channel.name, message) } -func RplJoin(channel *Channel, user *User) Reply { - return NewStringReply(user, RPL_JOIN, channel.name) +func RplJoin(channel *Channel, client *Client) Reply { + return NewStringReply(client, RPL_JOIN, channel.name) } -func RplPart(channel *Channel, user *User, message string) Reply { - return NewStringReply(user, RPL_PART, "%s :%s", channel.name, message) +func RplPart(channel *Channel, client *Client, message string) Reply { + return NewStringReply(client, RPL_PART, "%s :%s", channel.name, message) } func RplPong(server *Server) Reply { diff --git a/src/irc/server.go b/src/irc/server.go index 39e7c750..f0a14876 100644 --- a/src/irc/server.go +++ b/src/irc/server.go @@ -2,7 +2,6 @@ package irc import ( "code.google.com/p/go.crypto/bcrypt" - "database/sql" "log" "net" "time" @@ -13,19 +12,16 @@ const ( ) type ChannelNameMap map[string]*Channel -type UserNameMap map[string]*User -type ServiceNameMap map[string]Service +type ClientNameMap map[string]*Client type Server struct { - hostname string + channels ChannelNameMap + commands chan<- Command ctime time.Time + hostname string name string password []byte - users UserNameMap - channels ChannelNameMap - services ServiceNameMap - commands chan<- Command - db *sql.DB + clients ClientNameMap } func NewServer(name string) *Server { @@ -34,43 +30,13 @@ func NewServer(name string) *Server { ctime: time.Now(), name: name, commands: commands, - users: make(UserNameMap), + clients: make(ClientNameMap), channels: make(ChannelNameMap), - services: make(ServiceNameMap), - db: NewDatabase(), } go server.receiveCommands(commands) - NewNickServ(server) - Load(server.db, server) return server } -func (server *Server) Load(q Queryable) bool { - crs, err := FindAllChannels(q) - if err != nil { - log.Println(err) - return false - } - for _, cr := range crs { - channel := server.GetOrMakeChannel(cr.name) - channel.id = &(cr.id) - } - - urs, err := FindAllUsers(q) - if err != nil { - log.Println(err) - return false - } - for _, ur := range urs { - user := NewUser(ur.nick, server) - user.SetHash(ur.hash) - if !user.Load(q) { - return false - } - } - return true -} - func (server *Server) receiveCommands(commands <-chan Command) { for command := range commands { if DEBUG_SERVER { @@ -113,16 +79,16 @@ 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 { - users := make(UserSet) - users.Add(fromUser) - for channel := range fromUser.channels { - for user := range channel.members { - users.Add(user) +func (s *Server) interestedClients(fromClient *Client) ClientSet { + clients := make(ClientSet) + clients[fromClient] = true + for channel := range fromClient.channels { + for client := range channel.members { + clients[client] = true } } - return users + return clients } // server functionality @@ -164,9 +130,6 @@ func (s *Server) Nick() string { func (s *Server) DeleteChannel(channel *Channel) { delete(s.channels, channel.name) - if err := DeleteChannel(s.db, channel); err != nil { - log.Println(err) - } } // @@ -198,32 +161,30 @@ 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.nick = m.nickname - s.tryRegister(c) + + if s.clients[m.nickname] != nil { + c.replies <- ErrNickNameInUse(s, m.nickname) return } - user := c.user - if s.users[m.nickname] != nil { - user.Replies() <- ErrNickNameInUse(s, m.nickname) - return + reply := RplNick(c, m.nickname) + for iclient := range s.interestedClients(c) { + iclient.replies <- reply } - delete(s.users, user.nick) - s.users[m.nickname] = user - reply := RplNick(user, m.nickname) - for iuser := range s.InterestedUsers(user) { - iuser.Replies() <- reply + if c.HasNick() { + delete(s.clients, c.nick) } - user.nick = m.nickname + s.clients[m.nickname] = c + c.nick = m.nickname + + s.tryRegister(c) } func (m *UserMsgCommand) HandleServer(s *Server) { c := m.Client() - if c.username != "" { - c.Replies() <- ErrAlreadyRegistered(s) + if c.registered { + c.replies <- ErrAlreadyRegistered(s) return } @@ -234,108 +195,65 @@ func (m *UserMsgCommand) HandleServer(s *Server) { func (m *QuitCommand) HandleServer(s *Server) { c := m.Client() - user := c.user - if user != nil { - reply := RplQuit(c, m.message) - for user := range s.InterestedUsers(c.user) { - user.Replies() <- reply - } + reply := RplQuit(c, m.message) + for client := range s.interestedClients(c) { + client.replies <- reply } c.conn.Close() - if user == nil { - return + cmd := &PartCommand{ + BaseCommand: BaseCommand{c}, } - - user.LogoutClient(c) - if !user.HasClients() { - cmd := &PartCommand{ - BaseCommand: BaseCommand{c}, - } - for channel := range user.channels { - channel.Commands() <- cmd - } + for channel := range c.channels { + channel.commands <- cmd } } func (m *JoinCommand) HandleServer(s *Server) { c := m.Client() - if c.user == nil { - c.Replies() <- ErrNoPrivileges(s) - return - } - if m.zero { cmd := &PartCommand{ BaseCommand: BaseCommand{c}, } - for channel := range c.user.channels { - channel.Commands() <- cmd + for channel := range c.channels { + channel.commands <- cmd } return } for name := range m.channels { - s.GetOrMakeChannel(name).Commands() <- m + s.GetOrMakeChannel(name).commands <- m } } func (m *PartCommand) HandleServer(s *Server) { - user := m.User() - - if user == nil { - m.Client().Replies() <- ErrNoPrivileges(s) - return - } - for _, chname := range m.channels { channel := s.channels[chname] if channel == nil { - user.Replies() <- ErrNoSuchChannel(s, channel.name) + m.Client().replies <- ErrNoSuchChannel(s, channel.name) continue } - channel.Commands() <- m + channel.commands <- m } } func (m *TopicCommand) HandleServer(s *Server) { - user := m.User() - - // Hide all channels from logged-out clients. - if user == nil { - m.Client().Replies() <- ErrNoPrivileges(s) - return - } - channel := s.channels[m.channel] if channel == nil { - m.Client().Replies() <- ErrNoSuchChannel(s, m.channel) + m.Client().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 - return - } - - user := m.User() - // Hide all users from logged-out clients. - if user == nil { - m.Client().Replies() <- ErrNoPrivileges(s) - return - } - if m.TargetIsChannel() { channel := s.channels[m.target] if channel == nil { - m.Client().Replies() <- ErrNoSuchChannel(s, m.target) + m.Client().replies <- ErrNoSuchChannel(s, m.target) return } @@ -343,9 +261,9 @@ func (m *PrivMsgCommand) HandleServer(s *Server) { return } - target := s.users[m.target] + target := s.clients[m.target] if target == nil { - m.Client().Replies() <- ErrNoSuchNick(s, m.target) + m.Client().replies <- ErrNoSuchNick(s, m.target) return } @@ -353,5 +271,5 @@ func (m *PrivMsgCommand) HandleServer(s *Server) { } 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 deleted file mode 100644 index 162287b2..00000000 --- a/src/irc/service.go +++ /dev/null @@ -1,83 +0,0 @@ -package irc - -import ( - "fmt" - "log" -) - -type ServiceCommand interface { - Command - HandleService(Service) -} - -type Service interface { - Identifier - Commands() chan<- ServiceCommand - HandlePrivMsg(*PrivMsgCommand) - Debug() bool -} - -type EditableService interface { - Service - SetBase(*BaseService) -} - -type BaseService struct { - server *Server - name string - commands chan<- ServiceCommand -} - -func NewService(service EditableService, s *Server, name string) Service { - commands := make(chan ServiceCommand) - base := &BaseService{ - server: s, - name: name, - commands: commands, - } - go receiveCommands(service, commands) - service.SetBase(base) - s.services[service.Nick()] = service - return service -} - -func receiveCommands(service Service, commands <-chan ServiceCommand) { - for command := range commands { - if service.Debug() { - log.Printf("%s ← %s %s", service.Id(), command.Client(), command) - } - command.HandleService(service) - } -} - -func (service *BaseService) Id() string { - return fmt.Sprintf("%s!%s@%s", service.name, service.name, service.server.name) -} - -func (service *BaseService) String() string { - return service.Id() -} - -func (service *BaseService) PublicId() string { - return service.Id() -} - -func (service *BaseService) Nick() string { - return service.name -} - -func (service *BaseService) Reply(client *Client, message string) { - client.Replies() <- RplPrivMsg(service, client, message) -} - -func (service *BaseService) Commands() chan<- ServiceCommand { - return service.commands -} - -// -// commands -// - -func (m *PrivMsgCommand) HandleService(service Service) { - service.HandlePrivMsg(m) -} diff --git a/src/irc/user.go b/src/irc/user.go deleted file mode 100644 index 6064c939..00000000 --- a/src/irc/user.go +++ /dev/null @@ -1,228 +0,0 @@ -package irc - -import ( - "code.google.com/p/go.crypto/bcrypt" - "fmt" - "log" -) - -const ( - DEBUG_USER = true -) - -type UserCommand interface { - Command - HandleUser(*User) -} - -type User struct { - id RowId - nick string - hash []byte - server *Server - clients ClientSet - channels ChannelSet - commands chan<- UserCommand - replies chan<- Reply -} - -type UserSet map[*User]bool - -func (set UserSet) Add(user *User) { - set[user] = true -} - -func (set UserSet) Remove(user *User) { - delete(set, user) -} - -func (set UserSet) Nicks() []string { - nicks := make([]string, len(set)) - i := 0 - for member := range set { - nicks[i] = member.Nick() - i++ - } - return nicks -} - -func NewUser(nick string, server *Server) *User { - commands := make(chan UserCommand) - replies := make(chan Reply) - user := &User{ - nick: nick, - server: server, - clients: make(ClientSet), - channels: make(ChannelSet), - replies: replies, - } - - go user.receiveCommands(commands) - go user.receiveReplies(replies) - server.users[nick] = user - - return user -} - -func (user *User) Row() *UserRow { - return &UserRow{user.id, user.nick, user.hash} -} - -func (user *User) Create(q Queryable) bool { - var err error - if err := InsertUser(q, user.Row()); err != nil { - log.Println(err) - return false - } - user.id, err = FindUserIdByNick(q, user.nick) - if err != nil { - log.Println(err) - return false - } - return true -} - -func (user *User) Save(q Queryable) bool { - if err := UpdateUser(q, user.Row()); err != nil { - log.Println(err) - return false - } - - channelIds := user.channels.Ids() - if len(channelIds) == 0 { - if err := DeleteAllUserChannels(q, user.id); err != nil { - log.Println(err) - return false - } - } else { - if err := DeleteOtherUserChannels(q, user.id, channelIds); err != nil { - log.Println(err) - return false - } - if err := InsertUserChannels(q, user.id, channelIds); err != nil { - log.Println(err) - return false - } - } - return true -} - -func (user *User) Delete(q Queryable) bool { - err := DeleteUser(q, user.id) - if err != nil { - log.Println(err) - return false - } - return true -} - -func (user *User) Load(q Queryable) bool { - crs, err := FindChannelsForUser(q, user.id) - if err != nil { - log.Println(err) - return false - } - for _, cr := range crs { - user.server.GetOrMakeChannel(cr.name).Join(user) - } - return true -} - -func (user *User) SetPassword(password string) { - hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) - if err != nil { - log.Panicln(err) - } - user.SetHash(hash) -} - -func (user *User) SetHash(hash []byte) { - user.hash = hash -} - -func (user *User) receiveCommands(commands <-chan UserCommand) { - for command := range commands { - if DEBUG_USER { - log.Printf("%s → %s : %s", command.Client(), user, command) - } - command.HandleUser(user) - } -} - -// Distribute replies to clients. -func (user *User) receiveReplies(replies <-chan Reply) { - for reply := range replies { - if DEBUG_USER { - log.Printf("%s ← %s : %s", user, reply.Source(), reply) - } - for client := range user.clients { - client.Replies() <- reply - } - } -} - -// Identifier - -func (user *User) Id() string { - return fmt.Sprintf("%s!%s@%s", user.nick, user.nick, user.server.Id()) -} - -func (user *User) PublicId() string { - return user.Id() -} - -func (user *User) Nick() string { - return user.nick -} - -func (user *User) String() string { - return user.Id() -} - -func (user *User) Login(c *Client, nick string, password string) bool { - if nick != c.nick { - return false - } - - if user.hash == nil { - return false - } - - err := bcrypt.CompareHashAndPassword(user.hash, []byte(password)) - if err != nil { - c.Replies() <- ErrNoPrivileges(user.server) - return false - } - - user.clients[c] = true - c.user = user - for channel := range user.channels { - channel.GetTopic(c) - channel.GetUsers(c) - } - return true -} - -func (user *User) LogoutClient(c *Client) bool { - if user.clients[c] { - delete(user.clients, c) - return true - } - return false -} - -func (user *User) HasClients() bool { - return len(user.clients) > 0 -} - -func (user *User) Replies() chan<- Reply { - return user.replies -} - -// -// commands -// - -func (m *PrivMsgCommand) HandleUser(user *User) { - user.Replies() <- RplPrivMsg(m.Client(), user, m.message) -}