mirror of
https://github.com/ergochat/ergo.git
synced 2024-12-23 03:02:48 +01:00
get rid of user abstraction, services
This commit is contained in:
parent
f133f3691d
commit
f04dd7c5d5
6
build.sh
6
build.sh
@ -1,5 +1,5 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
export GOPATH="$PWD"
|
export GOPATH="$PWD"
|
||||||
go get "code.google.com/p/go.crypto/bcrypt"
|
#go get "code.google.com/p/go.crypto/bcrypt"
|
||||||
go get "github.com/mattn/go-sqlite3"
|
#go get "github.com/mattn/go-sqlite3"
|
||||||
go install ergonomadic genpasswd ergonomadicdb
|
go install ergonomadic genpasswd
|
||||||
|
@ -9,82 +9,41 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Channel struct {
|
type Channel struct {
|
||||||
id *RowId
|
|
||||||
server *Server
|
|
||||||
commands chan<- ChannelCommand
|
commands chan<- ChannelCommand
|
||||||
replies chan<- Reply
|
|
||||||
name string
|
|
||||||
key string
|
key string
|
||||||
topic string
|
members ClientSet
|
||||||
members UserSet
|
name string
|
||||||
noOutside bool
|
noOutside bool
|
||||||
password string
|
password string
|
||||||
|
replies chan<- Reply
|
||||||
|
server *Server
|
||||||
|
topic string
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChannelSet map[*Channel]bool
|
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 {
|
type ChannelCommand interface {
|
||||||
Command
|
Command
|
||||||
HandleChannel(channel *Channel)
|
HandleChannel(channel *Channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewChannel creates a new channel from a `Server` and a `name` string, which
|
// NewChannel creates a new channel from a `Server` and a `name`
|
||||||
// must be unique on the server.
|
// string, which must be unique on the server.
|
||||||
func NewChannel(s *Server, name string) *Channel {
|
func NewChannel(s *Server, name string) *Channel {
|
||||||
commands := make(chan ChannelCommand)
|
commands := make(chan ChannelCommand)
|
||||||
replies := make(chan Reply)
|
replies := make(chan Reply)
|
||||||
channel := &Channel{
|
channel := &Channel{
|
||||||
name: name,
|
name: name,
|
||||||
members: make(UserSet),
|
members: make(ClientSet),
|
||||||
server: s,
|
server: s,
|
||||||
commands: commands,
|
commands: commands,
|
||||||
replies: replies,
|
replies: replies,
|
||||||
}
|
}
|
||||||
go channel.receiveCommands(commands)
|
go channel.receiveCommands(commands)
|
||||||
go channel.receiveReplies(replies)
|
go channel.receiveReplies(replies)
|
||||||
Save(s.db, channel)
|
|
||||||
return 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) {
|
func (channel *Channel) receiveCommands(commands <-chan ChannelCommand) {
|
||||||
for command := range commands {
|
for command := range commands {
|
||||||
if DEBUG_CHANNEL {
|
if DEBUG_CHANNEL {
|
||||||
@ -99,16 +58,13 @@ func (channel *Channel) receiveReplies(replies <-chan Reply) {
|
|||||||
if DEBUG_CHANNEL {
|
if DEBUG_CHANNEL {
|
||||||
log.Printf("%s ← %s : %s", channel, reply.Source(), reply)
|
log.Printf("%s ← %s : %s", channel, reply.Source(), reply)
|
||||||
}
|
}
|
||||||
for user := range channel.members {
|
for client := range channel.members {
|
||||||
if user != reply.Source() {
|
if client != reply.Source() {
|
||||||
user.Replies() <- reply
|
client.replies <- reply
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func (channel *Channel) Nicks() []string {
|
|
||||||
return channel.members.Nicks()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (channel *Channel) IsEmpty() bool {
|
func (channel *Channel) IsEmpty() bool {
|
||||||
return len(channel.members) == 0
|
return len(channel.members) == 0
|
||||||
@ -127,6 +83,16 @@ func (channel *Channel) GetUsers(replier Replier) {
|
|||||||
replier.Replies() <- NewNamesReply(channel)
|
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 {
|
func (channel *Channel) Replies() chan<- Reply {
|
||||||
return channel.replies
|
return channel.replies
|
||||||
}
|
}
|
||||||
@ -143,20 +109,22 @@ func (channel *Channel) PublicId() string {
|
|||||||
return channel.name
|
return channel.name
|
||||||
}
|
}
|
||||||
|
|
||||||
func (channel *Channel) Commands() chan<- ChannelCommand {
|
|
||||||
return channel.commands
|
|
||||||
}
|
|
||||||
|
|
||||||
func (channel *Channel) String() string {
|
func (channel *Channel) String() string {
|
||||||
return channel.Id()
|
return channel.Id()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (channel *Channel) Join(user *User) {
|
func (channel *Channel) Join(client *Client) {
|
||||||
channel.members.Add(user)
|
channel.members[client] = true
|
||||||
user.channels.Add(channel)
|
client.channels[channel] = true
|
||||||
channel.Replies() <- RplJoin(channel, user)
|
reply := RplJoin(channel, client)
|
||||||
channel.GetTopic(user)
|
client.replies <- reply
|
||||||
channel.GetUsers(user)
|
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) {
|
func (m *JoinCommand) HandleChannel(channel *Channel) {
|
||||||
client := m.Client()
|
client := m.Client()
|
||||||
if channel.key != m.channels[channel.name] {
|
if channel.key != m.channels[channel.name] {
|
||||||
client.Replies() <- ErrBadChannelKey(channel)
|
client.replies <- ErrBadChannelKey(channel)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
channel.Join(client.user)
|
channel.Join(client)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *PartCommand) HandleChannel(channel *Channel) {
|
func (m *PartCommand) HandleChannel(channel *Channel) {
|
||||||
user := m.Client().user
|
c := m.Client()
|
||||||
|
|
||||||
if !channel.members[user] {
|
if !channel.HasMember(c) {
|
||||||
user.Replies() <- ErrNotOnChannel(channel)
|
c.replies <- ErrNotOnChannel(channel)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
msg := m.message
|
msg := m.message
|
||||||
if msg == "" {
|
if msg == "" {
|
||||||
msg = user.Nick()
|
msg = c.Nick()
|
||||||
}
|
}
|
||||||
|
|
||||||
channel.Replies() <- RplPart(channel, user, msg)
|
channel.replies <- RplPart(channel, c, msg)
|
||||||
|
|
||||||
channel.members.Remove(user)
|
delete(channel.members, c)
|
||||||
user.channels.Remove(channel)
|
delete(c.channels, channel)
|
||||||
|
|
||||||
if channel.IsEmpty() {
|
if channel.IsEmpty() { // TODO persistent channels
|
||||||
channel.server.DeleteChannel(channel)
|
channel.server.DeleteChannel(channel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *TopicCommand) HandleChannel(channel *Channel) {
|
func (m *TopicCommand) HandleChannel(channel *Channel) {
|
||||||
user := m.User()
|
client := m.Client()
|
||||||
|
|
||||||
if !channel.members[user] {
|
if !channel.HasMember(client) {
|
||||||
user.Replies() <- ErrNotOnChannel(channel)
|
client.replies <- ErrNotOnChannel(channel)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.topic == "" {
|
if m.topic == "" {
|
||||||
channel.GetTopic(user)
|
channel.GetTopic(client)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
channel.topic = m.topic
|
channel.topic = m.topic
|
||||||
|
|
||||||
if channel.topic == "" {
|
channel.GetTopic(channel)
|
||||||
channel.Replies() <- RplNoTopic(channel)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
channel.Replies() <- RplTopic(channel)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *PrivMsgCommand) HandleChannel(channel *Channel) {
|
func (m *PrivMsgCommand) HandleChannel(channel *Channel) {
|
||||||
channel.Replies() <- RplPrivMsgChannel(channel, m.User(), m.message)
|
channel.replies <- RplPrivMsgChannel(channel, m.Client(), m.message)
|
||||||
}
|
}
|
||||||
|
@ -8,22 +8,28 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DEBUG_CLIENT = true
|
DEBUG_CLIENT = false
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type ClientCommand interface {
|
||||||
|
Command
|
||||||
|
HandleClient(*Client)
|
||||||
|
}
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
|
atime time.Time
|
||||||
|
away bool
|
||||||
|
channels ChannelSet
|
||||||
|
commands chan<- ClientCommand
|
||||||
conn net.Conn
|
conn net.Conn
|
||||||
username string
|
|
||||||
realname string
|
|
||||||
hostname string
|
hostname string
|
||||||
nick string
|
nick string
|
||||||
serverPass bool
|
realname string
|
||||||
registered bool
|
registered bool
|
||||||
away bool
|
|
||||||
server *Server
|
|
||||||
atime time.Time
|
|
||||||
user *User
|
|
||||||
replies chan<- Reply
|
replies chan<- Reply
|
||||||
|
server *Server
|
||||||
|
serverPass bool
|
||||||
|
username string
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClientSet map[*Client]bool
|
type ClientSet map[*Client]bool
|
||||||
@ -31,17 +37,21 @@ type ClientSet map[*Client]bool
|
|||||||
func NewClient(server *Server, conn net.Conn) *Client {
|
func NewClient(server *Server, conn net.Conn) *Client {
|
||||||
read := StringReadChan(conn)
|
read := StringReadChan(conn)
|
||||||
write := StringWriteChan(conn)
|
write := StringWriteChan(conn)
|
||||||
|
commands := make(chan ClientCommand)
|
||||||
replies := make(chan Reply)
|
replies := make(chan Reply)
|
||||||
|
|
||||||
client := &Client{
|
client := &Client{
|
||||||
|
channels: make(ChannelSet),
|
||||||
|
commands: commands,
|
||||||
conn: conn,
|
conn: conn,
|
||||||
hostname: conn.RemoteAddr().String(),
|
hostname: LookupHostname(conn.RemoteAddr()),
|
||||||
server: server,
|
|
||||||
replies: replies,
|
replies: replies,
|
||||||
|
server: server,
|
||||||
}
|
}
|
||||||
|
|
||||||
go client.readConn(read)
|
go client.readConn(read)
|
||||||
go client.writeConn(write, replies)
|
go client.writeConn(write, replies)
|
||||||
|
go client.receiveCommands(commands)
|
||||||
|
|
||||||
return client
|
return client
|
||||||
}
|
}
|
||||||
@ -51,9 +61,9 @@ func (c *Client) readConn(recv <-chan string) {
|
|||||||
m, err := ParseCommand(str)
|
m, err := ParseCommand(str)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == NotEnoughArgsError {
|
if err == NotEnoughArgsError {
|
||||||
c.Replies() <- ErrNeedMoreParams(c.server, str)
|
c.replies <- ErrNeedMoreParams(c.server, str)
|
||||||
} else {
|
} else {
|
||||||
c.Replies() <- ErrUnknownCommand(c.server, str)
|
c.replies <- ErrUnknownCommand(c.server, str)
|
||||||
}
|
}
|
||||||
continue
|
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 {
|
func (c *Client) Replies() chan<- Reply {
|
||||||
return c.replies
|
return c.replies
|
||||||
}
|
}
|
||||||
@ -81,11 +100,7 @@ func (c *Client) Server() *Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) Nick() string {
|
func (c *Client) Nick() string {
|
||||||
if c.user != nil {
|
if c.HasNick() {
|
||||||
return c.user.Nick()
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.nick != "" {
|
|
||||||
return c.nick
|
return c.nick
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,7 +112,7 @@ func (c *Client) UModeString() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) HasNick() bool {
|
func (c *Client) HasNick() bool {
|
||||||
return c.Nick() != ""
|
return c.nick != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) HasUsername() bool {
|
func (c *Client) HasUsername() bool {
|
||||||
@ -126,3 +141,11 @@ func (c *Client) String() string {
|
|||||||
func (c *Client) PublicId() string {
|
func (c *Client) PublicId() string {
|
||||||
return fmt.Sprintf("%s!%s@%s", c.Nick(), c.Nick(), c.server.Id())
|
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)
|
||||||
|
}
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
|
|
||||||
type Command interface {
|
type Command interface {
|
||||||
Client() *Client
|
Client() *Client
|
||||||
User() *User
|
|
||||||
Source() Identifier
|
Source() Identifier
|
||||||
Reply(Reply)
|
Reply(Reply)
|
||||||
HandleServer(*Server)
|
HandleServer(*Server)
|
||||||
@ -46,10 +45,6 @@ func (command *BaseCommand) Client() *Client {
|
|||||||
return command.client
|
return command.client
|
||||||
}
|
}
|
||||||
|
|
||||||
func (command *BaseCommand) User() *User {
|
|
||||||
return command.Client().user
|
|
||||||
}
|
|
||||||
|
|
||||||
func (command *BaseCommand) SetBase(c *Client) {
|
func (command *BaseCommand) SetBase(c *Client) {
|
||||||
*command = BaseCommand{c}
|
*command = BaseCommand{c}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DEBUG_NET = true
|
DEBUG_NET = false
|
||||||
)
|
)
|
||||||
|
|
||||||
func readTrimmedLine(reader *bufio.Reader) (string, error) {
|
func readTrimmedLine(reader *bufio.Reader) (string, error) {
|
||||||
@ -64,7 +64,7 @@ func LookupHostname(addr net.Addr) string {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return addrStr
|
return addrStr
|
||||||
}
|
}
|
||||||
names, err := net.LookupAddr(ipaddr)
|
names, err := net.LookupHost(ipaddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ipaddr
|
return ipaddr
|
||||||
}
|
}
|
||||||
|
@ -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.")
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
@ -93,6 +93,7 @@ func NewNamesReply(channel *Channel) Reply {
|
|||||||
BaseReply: &BaseReply{
|
BaseReply: &BaseReply{
|
||||||
source: channel,
|
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)
|
return NewStringReply(source, RPL_PRIVMSG, "%s :%s", channel.name, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RplJoin(channel *Channel, user *User) Reply {
|
func RplJoin(channel *Channel, client *Client) Reply {
|
||||||
return NewStringReply(user, RPL_JOIN, channel.name)
|
return NewStringReply(client, RPL_JOIN, channel.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RplPart(channel *Channel, user *User, message string) Reply {
|
func RplPart(channel *Channel, client *Client, message string) Reply {
|
||||||
return NewStringReply(user, RPL_PART, "%s :%s", channel.name, message)
|
return NewStringReply(client, RPL_PART, "%s :%s", channel.name, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RplPong(server *Server) Reply {
|
func RplPong(server *Server) Reply {
|
||||||
|
@ -2,7 +2,6 @@ package irc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"code.google.com/p/go.crypto/bcrypt"
|
"code.google.com/p/go.crypto/bcrypt"
|
||||||
"database/sql"
|
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
@ -13,19 +12,16 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type ChannelNameMap map[string]*Channel
|
type ChannelNameMap map[string]*Channel
|
||||||
type UserNameMap map[string]*User
|
type ClientNameMap map[string]*Client
|
||||||
type ServiceNameMap map[string]Service
|
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
hostname string
|
channels ChannelNameMap
|
||||||
|
commands chan<- Command
|
||||||
ctime time.Time
|
ctime time.Time
|
||||||
|
hostname string
|
||||||
name string
|
name string
|
||||||
password []byte
|
password []byte
|
||||||
users UserNameMap
|
clients ClientNameMap
|
||||||
channels ChannelNameMap
|
|
||||||
services ServiceNameMap
|
|
||||||
commands chan<- Command
|
|
||||||
db *sql.DB
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServer(name string) *Server {
|
func NewServer(name string) *Server {
|
||||||
@ -34,43 +30,13 @@ func NewServer(name string) *Server {
|
|||||||
ctime: time.Now(),
|
ctime: time.Now(),
|
||||||
name: name,
|
name: name,
|
||||||
commands: commands,
|
commands: commands,
|
||||||
users: make(UserNameMap),
|
clients: make(ClientNameMap),
|
||||||
channels: make(ChannelNameMap),
|
channels: make(ChannelNameMap),
|
||||||
services: make(ServiceNameMap),
|
|
||||||
db: NewDatabase(),
|
|
||||||
}
|
}
|
||||||
go server.receiveCommands(commands)
|
go server.receiveCommands(commands)
|
||||||
NewNickServ(server)
|
|
||||||
Load(server.db, server)
|
|
||||||
return 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) {
|
func (server *Server) receiveCommands(commands <-chan Command) {
|
||||||
for command := range commands {
|
for command := range commands {
|
||||||
if DEBUG_SERVER {
|
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.
|
// Send a message to clients of channels fromClient is a member.
|
||||||
func (s *Server) InterestedUsers(fromUser *User) UserSet {
|
func (s *Server) interestedClients(fromClient *Client) ClientSet {
|
||||||
users := make(UserSet)
|
clients := make(ClientSet)
|
||||||
users.Add(fromUser)
|
clients[fromClient] = true
|
||||||
for channel := range fromUser.channels {
|
for channel := range fromClient.channels {
|
||||||
for user := range channel.members {
|
for client := range channel.members {
|
||||||
users.Add(user)
|
clients[client] = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return users
|
return clients
|
||||||
}
|
}
|
||||||
|
|
||||||
// server functionality
|
// server functionality
|
||||||
@ -164,9 +130,6 @@ func (s *Server) Nick() string {
|
|||||||
|
|
||||||
func (s *Server) DeleteChannel(channel *Channel) {
|
func (s *Server) DeleteChannel(channel *Channel) {
|
||||||
delete(s.channels, channel.name)
|
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) {
|
func (m *NickCommand) HandleServer(s *Server) {
|
||||||
c := m.Client()
|
c := m.Client()
|
||||||
if c.user == nil {
|
|
||||||
c.Replies() <- RplNick(c, m.nickname)
|
if s.clients[m.nickname] != nil {
|
||||||
|
c.replies <- ErrNickNameInUse(s, m.nickname)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
reply := RplNick(c, m.nickname)
|
||||||
|
for iclient := range s.interestedClients(c) {
|
||||||
|
iclient.replies <- reply
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.HasNick() {
|
||||||
|
delete(s.clients, c.nick)
|
||||||
|
}
|
||||||
|
s.clients[m.nickname] = c
|
||||||
c.nick = m.nickname
|
c.nick = m.nickname
|
||||||
|
|
||||||
s.tryRegister(c)
|
s.tryRegister(c)
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
user := c.user
|
|
||||||
if s.users[m.nickname] != nil {
|
|
||||||
user.Replies() <- ErrNickNameInUse(s, m.nickname)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
delete(s.users, user.nick)
|
|
||||||
s.users[m.nickname] = user
|
|
||||||
reply := RplNick(user, m.nickname)
|
|
||||||
for iuser := range s.InterestedUsers(user) {
|
|
||||||
iuser.Replies() <- reply
|
|
||||||
}
|
|
||||||
user.nick = m.nickname
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *UserMsgCommand) HandleServer(s *Server) {
|
func (m *UserMsgCommand) HandleServer(s *Server) {
|
||||||
c := m.Client()
|
c := m.Client()
|
||||||
if c.username != "" {
|
if c.registered {
|
||||||
c.Replies() <- ErrAlreadyRegistered(s)
|
c.replies <- ErrAlreadyRegistered(s)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,108 +195,65 @@ func (m *UserMsgCommand) HandleServer(s *Server) {
|
|||||||
func (m *QuitCommand) HandleServer(s *Server) {
|
func (m *QuitCommand) HandleServer(s *Server) {
|
||||||
c := m.Client()
|
c := m.Client()
|
||||||
|
|
||||||
user := c.user
|
|
||||||
if user != nil {
|
|
||||||
reply := RplQuit(c, m.message)
|
reply := RplQuit(c, m.message)
|
||||||
for user := range s.InterestedUsers(c.user) {
|
for client := range s.interestedClients(c) {
|
||||||
user.Replies() <- reply
|
client.replies <- reply
|
||||||
}
|
|
||||||
}
|
}
|
||||||
c.conn.Close()
|
c.conn.Close()
|
||||||
if user == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
user.LogoutClient(c)
|
|
||||||
if !user.HasClients() {
|
|
||||||
cmd := &PartCommand{
|
cmd := &PartCommand{
|
||||||
BaseCommand: BaseCommand{c},
|
BaseCommand: BaseCommand{c},
|
||||||
}
|
}
|
||||||
for channel := range user.channels {
|
for channel := range c.channels {
|
||||||
channel.Commands() <- cmd
|
channel.commands <- cmd
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *JoinCommand) HandleServer(s *Server) {
|
func (m *JoinCommand) HandleServer(s *Server) {
|
||||||
c := m.Client()
|
c := m.Client()
|
||||||
|
|
||||||
if c.user == nil {
|
|
||||||
c.Replies() <- ErrNoPrivileges(s)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if m.zero {
|
if m.zero {
|
||||||
cmd := &PartCommand{
|
cmd := &PartCommand{
|
||||||
BaseCommand: BaseCommand{c},
|
BaseCommand: BaseCommand{c},
|
||||||
}
|
}
|
||||||
for channel := range c.user.channels {
|
for channel := range c.channels {
|
||||||
channel.Commands() <- cmd
|
channel.commands <- cmd
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for name := range m.channels {
|
for name := range m.channels {
|
||||||
s.GetOrMakeChannel(name).Commands() <- m
|
s.GetOrMakeChannel(name).commands <- m
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *PartCommand) HandleServer(s *Server) {
|
func (m *PartCommand) HandleServer(s *Server) {
|
||||||
user := m.User()
|
|
||||||
|
|
||||||
if user == nil {
|
|
||||||
m.Client().Replies() <- ErrNoPrivileges(s)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, chname := range m.channels {
|
for _, chname := range m.channels {
|
||||||
channel := s.channels[chname]
|
channel := s.channels[chname]
|
||||||
|
|
||||||
if channel == nil {
|
if channel == nil {
|
||||||
user.Replies() <- ErrNoSuchChannel(s, channel.name)
|
m.Client().replies <- ErrNoSuchChannel(s, channel.name)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
channel.Commands() <- m
|
channel.commands <- m
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *TopicCommand) HandleServer(s *Server) {
|
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]
|
channel := s.channels[m.channel]
|
||||||
if channel == nil {
|
if channel == nil {
|
||||||
m.Client().Replies() <- ErrNoSuchChannel(s, m.channel)
|
m.Client().replies <- ErrNoSuchChannel(s, m.channel)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
channel.Commands() <- m
|
channel.commands <- m
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *PrivMsgCommand) HandleServer(s *Server) {
|
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() {
|
if m.TargetIsChannel() {
|
||||||
channel := s.channels[m.target]
|
channel := s.channels[m.target]
|
||||||
if channel == nil {
|
if channel == nil {
|
||||||
m.Client().Replies() <- ErrNoSuchChannel(s, m.target)
|
m.Client().replies <- ErrNoSuchChannel(s, m.target)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -343,9 +261,9 @@ func (m *PrivMsgCommand) HandleServer(s *Server) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
target := s.users[m.target]
|
target := s.clients[m.target]
|
||||||
if target == nil {
|
if target == nil {
|
||||||
m.Client().Replies() <- ErrNoSuchNick(s, m.target)
|
m.Client().replies <- ErrNoSuchNick(s, m.target)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -353,5 +271,5 @@ func (m *PrivMsgCommand) HandleServer(s *Server) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *ModeCommand) HandleServer(s *Server) {
|
func (m *ModeCommand) HandleServer(s *Server) {
|
||||||
m.Client().Replies() <- RplUModeIs(s, m.Client())
|
m.Client().replies <- RplUModeIs(s, m.Client())
|
||||||
}
|
}
|
||||||
|
@ -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)
|
|
||||||
}
|
|
228
src/irc/user.go
228
src/irc/user.go
@ -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)
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user