2012-04-07 20:44:59 +02:00
|
|
|
package irc
|
|
|
|
|
|
|
|
import (
|
2012-12-15 23:34:20 +01:00
|
|
|
"code.google.com/p/go.crypto/bcrypt"
|
2013-06-03 01:32:22 +02:00
|
|
|
"database/sql"
|
2012-04-07 20:44:59 +02:00
|
|
|
"log"
|
|
|
|
"net"
|
2012-04-18 07:21:41 +02:00
|
|
|
"time"
|
2012-04-07 20:44:59 +02:00
|
|
|
)
|
|
|
|
|
2013-05-11 23:43:06 +02:00
|
|
|
const (
|
|
|
|
DEBUG_SERVER = true
|
|
|
|
)
|
|
|
|
|
2012-12-15 23:34:20 +01:00
|
|
|
type ChannelNameMap map[string]*Channel
|
|
|
|
type UserNameMap map[string]*User
|
2013-05-11 22:55:01 +02:00
|
|
|
type ServiceNameMap map[string]Service
|
2012-12-17 04:13:53 +01:00
|
|
|
|
2012-04-07 20:44:59 +02:00
|
|
|
type Server struct {
|
2012-12-13 08:27:17 +01:00
|
|
|
hostname string
|
|
|
|
ctime time.Time
|
|
|
|
name string
|
2012-12-15 23:34:20 +01:00
|
|
|
password []byte
|
|
|
|
users UserNameMap
|
|
|
|
channels ChannelNameMap
|
2012-12-17 04:13:53 +01:00
|
|
|
services ServiceNameMap
|
|
|
|
commands chan<- Command
|
2013-06-03 01:32:22 +02:00
|
|
|
db *sql.DB
|
2012-04-18 03:16:57 +02:00
|
|
|
}
|
|
|
|
|
2012-04-18 07:11:35 +02:00
|
|
|
func NewServer(name string) *Server {
|
2013-06-03 01:53:06 +02:00
|
|
|
commands := make(chan Command)
|
2012-12-09 21:51:50 +01:00
|
|
|
server := &Server{
|
|
|
|
ctime: time.Now(),
|
|
|
|
name: name,
|
2012-12-15 23:34:20 +01:00
|
|
|
commands: commands,
|
|
|
|
users: make(UserNameMap),
|
|
|
|
channels: make(ChannelNameMap),
|
2012-12-17 04:13:53 +01:00
|
|
|
services: make(ServiceNameMap),
|
2013-05-26 22:28:22 +02:00
|
|
|
db: NewDatabase(),
|
2012-12-15 23:34:20 +01:00
|
|
|
}
|
|
|
|
go server.receiveCommands(commands)
|
2012-12-17 04:13:53 +01:00
|
|
|
NewNickServ(server)
|
2013-06-03 07:07:50 +02:00
|
|
|
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) {
|
2013-05-26 22:28:22 +02:00
|
|
|
return false
|
|
|
|
}
|
2013-06-03 07:07:50 +02:00
|
|
|
}
|
|
|
|
return true
|
2012-04-07 20:44:59 +02:00
|
|
|
}
|
|
|
|
|
2012-12-15 23:34:20 +01:00
|
|
|
func (server *Server) receiveCommands(commands <-chan Command) {
|
|
|
|
for command := range commands {
|
2013-05-11 23:43:06 +02:00
|
|
|
if DEBUG_SERVER {
|
2013-05-26 22:28:22 +02:00
|
|
|
log.Printf("%s → %s : %s", command.Client(), server, command)
|
2013-05-11 23:43:06 +02:00
|
|
|
}
|
2012-12-15 23:34:20 +01:00
|
|
|
command.Client().atime = time.Now()
|
2013-05-09 18:12:03 +02:00
|
|
|
command.HandleServer(server)
|
2012-12-15 23:34:20 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-04-07 20:44:59 +02:00
|
|
|
func (s *Server) Listen(addr string) {
|
|
|
|
listener, err := net.Listen("tcp", addr)
|
|
|
|
if err != nil {
|
2012-04-08 08:32:08 +02:00
|
|
|
log.Fatal("Server.Listen: ", err)
|
2012-04-07 20:44:59 +02:00
|
|
|
}
|
|
|
|
|
2012-12-09 07:54:58 +01:00
|
|
|
s.hostname = LookupHostname(listener.Addr())
|
|
|
|
log.Print("Server.Listen: listening on ", addr)
|
|
|
|
|
2012-04-07 20:44:59 +02:00
|
|
|
for {
|
|
|
|
conn, err := listener.Accept()
|
|
|
|
if err != nil {
|
2012-04-08 08:32:08 +02:00
|
|
|
log.Print("Server.Listen: ", err)
|
2012-04-07 20:44:59 +02:00
|
|
|
continue
|
|
|
|
}
|
2012-12-09 07:54:58 +01:00
|
|
|
log.Print("Server.Listen: accepted ", conn.RemoteAddr())
|
2012-12-15 23:34:20 +01:00
|
|
|
NewClient(s, conn)
|
2012-12-09 07:54:58 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Server) GetOrMakeChannel(name string) *Channel {
|
|
|
|
channel := s.channels[name]
|
|
|
|
|
|
|
|
if channel == nil {
|
|
|
|
channel = NewChannel(s, name)
|
|
|
|
s.channels[name] = channel
|
|
|
|
}
|
|
|
|
|
|
|
|
return channel
|
|
|
|
}
|
|
|
|
|
|
|
|
// Send a message to clients of channels fromClient is a member.
|
2013-05-12 20:20:55 +02:00
|
|
|
func (s *Server) InterestedUsers(fromUser *User) UserSet {
|
2012-12-15 23:34:20 +01:00
|
|
|
users := make(UserSet)
|
2012-12-17 04:13:53 +01:00
|
|
|
users.Add(fromUser)
|
2012-12-15 23:34:20 +01:00
|
|
|
for channel := range fromUser.channels {
|
|
|
|
for user := range channel.members {
|
2012-12-17 04:13:53 +01:00
|
|
|
users.Add(user)
|
2012-12-09 07:54:58 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-15 23:34:20 +01:00
|
|
|
return users
|
2012-04-07 20:44:59 +02:00
|
|
|
}
|
|
|
|
|
2012-12-09 07:54:58 +01:00
|
|
|
// server functionality
|
|
|
|
|
2012-12-15 23:34:20 +01:00
|
|
|
func (s *Server) tryRegister(c *Client) {
|
2013-05-12 20:20:55 +02:00
|
|
|
if !c.registered && c.HasNick() && c.HasUsername() && s.CheckPassword(c) {
|
2012-12-15 23:34:20 +01:00
|
|
|
c.registered = true
|
2013-05-12 20:20:55 +02:00
|
|
|
replies := []Reply{
|
|
|
|
RplWelcome(s, c),
|
2013-06-03 07:07:50 +02:00
|
|
|
RplYourHost(s),
|
2013-05-12 20:20:55 +02:00
|
|
|
RplCreated(s),
|
|
|
|
RplMyInfo(s),
|
|
|
|
}
|
2012-12-15 23:34:20 +01:00
|
|
|
for _, reply := range replies {
|
2013-05-09 20:05:10 +02:00
|
|
|
c.Replies() <- reply
|
2012-12-15 23:34:20 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-12 20:20:55 +02:00
|
|
|
func (s *Server) CheckPassword(c *Client) bool {
|
|
|
|
return (s.password == nil) || c.serverPass
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Server) Id() string {
|
2013-05-11 22:55:01 +02:00
|
|
|
return s.name
|
|
|
|
}
|
|
|
|
|
2013-05-12 03:28:18 +02:00
|
|
|
func (s *Server) String() string {
|
2013-05-11 22:55:01 +02:00
|
|
|
return s.Id()
|
2012-12-15 23:34:20 +01:00
|
|
|
}
|
|
|
|
|
2013-05-12 03:28:18 +02:00
|
|
|
func (s *Server) PublicId() string {
|
2012-12-15 23:34:20 +01:00
|
|
|
return s.Id()
|
|
|
|
}
|
|
|
|
|
2013-05-12 03:28:18 +02:00
|
|
|
func (s *Server) Nick() string {
|
2012-12-17 04:13:53 +01:00
|
|
|
return s.name
|
|
|
|
}
|
|
|
|
|
2012-12-15 23:34:20 +01:00
|
|
|
func (s *Server) DeleteChannel(channel *Channel) {
|
|
|
|
delete(s.channels, channel.name)
|
2013-06-03 01:32:22 +02:00
|
|
|
if err := DeleteChannel(s.db, channel); err != nil {
|
|
|
|
log.Println(err)
|
|
|
|
}
|
2012-12-15 23:34:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// commands
|
|
|
|
//
|
|
|
|
|
2013-05-12 03:28:18 +02:00
|
|
|
func (m *UnknownCommand) HandleServer(s *Server) {
|
2013-05-09 20:05:10 +02:00
|
|
|
m.Client().Replies() <- ErrUnknownCommand(s, m.command)
|
2012-12-17 04:13:53 +01:00
|
|
|
}
|
|
|
|
|
2013-05-09 18:12:03 +02:00
|
|
|
func (m *PingCommand) HandleServer(s *Server) {
|
2013-05-09 20:05:10 +02:00
|
|
|
m.Client().Replies() <- RplPong(s)
|
2012-12-15 23:34:20 +01:00
|
|
|
}
|
|
|
|
|
2013-05-09 18:12:03 +02:00
|
|
|
func (m *PongCommand) HandleServer(s *Server) {
|
2012-12-15 23:34:20 +01:00
|
|
|
// no-op
|
|
|
|
}
|
|
|
|
|
2013-05-09 18:12:03 +02:00
|
|
|
func (m *PassCommand) HandleServer(s *Server) {
|
2012-12-15 23:34:20 +01:00
|
|
|
err := bcrypt.CompareHashAndPassword(s.password, []byte(m.password))
|
|
|
|
if err != nil {
|
2013-05-09 20:05:10 +02:00
|
|
|
m.Client().Replies() <- ErrPasswdMismatch(s)
|
2012-12-09 07:54:58 +01:00
|
|
|
return
|
2012-04-09 16:57:55 +02:00
|
|
|
}
|
2012-12-09 07:54:58 +01:00
|
|
|
|
2012-12-15 23:34:20 +01:00
|
|
|
m.Client().serverPass = true
|
|
|
|
// no reply?
|
|
|
|
}
|
|
|
|
|
2013-05-09 18:12:03 +02:00
|
|
|
func (m *NickCommand) HandleServer(s *Server) {
|
2012-12-15 23:34:20 +01:00
|
|
|
c := m.Client()
|
|
|
|
if c.user == nil {
|
2013-05-09 20:05:10 +02:00
|
|
|
c.Replies() <- RplNick(c, m.nickname)
|
2012-12-15 23:34:20 +01:00
|
|
|
c.nick = m.nickname
|
|
|
|
s.tryRegister(c)
|
|
|
|
return
|
2012-12-09 07:54:58 +01:00
|
|
|
}
|
2012-12-09 21:51:50 +01:00
|
|
|
|
2013-05-12 20:20:55 +02:00
|
|
|
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
|
2012-12-09 07:54:58 +01:00
|
|
|
}
|
|
|
|
|
2013-05-09 18:12:03 +02:00
|
|
|
func (m *UserMsgCommand) HandleServer(s *Server) {
|
2012-12-15 23:34:20 +01:00
|
|
|
c := m.Client()
|
2012-12-09 07:54:58 +01:00
|
|
|
if c.username != "" {
|
2013-05-09 20:05:10 +02:00
|
|
|
c.Replies() <- ErrAlreadyRegistered(s)
|
2012-12-09 07:54:58 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2012-12-15 23:34:20 +01:00
|
|
|
c.username, c.realname = m.user, m.realname
|
2012-12-09 21:51:50 +01:00
|
|
|
s.tryRegister(c)
|
2012-12-09 07:54:58 +01:00
|
|
|
}
|
|
|
|
|
2013-05-09 18:12:03 +02:00
|
|
|
func (m *QuitCommand) HandleServer(s *Server) {
|
2012-12-15 23:34:20 +01:00
|
|
|
c := m.Client()
|
2012-12-17 04:13:53 +01:00
|
|
|
|
|
|
|
user := c.user
|
|
|
|
if user != nil {
|
|
|
|
reply := RplQuit(c, m.message)
|
|
|
|
for user := range s.InterestedUsers(c.user) {
|
2013-05-09 20:05:10 +02:00
|
|
|
user.Replies() <- reply
|
2012-12-17 04:13:53 +01:00
|
|
|
}
|
2012-12-15 23:34:20 +01:00
|
|
|
}
|
|
|
|
c.conn.Close()
|
2012-12-17 04:13:53 +01:00
|
|
|
if user == nil {
|
|
|
|
return
|
|
|
|
}
|
2012-12-15 23:34:20 +01:00
|
|
|
|
2012-12-17 04:13:53 +01:00
|
|
|
user.LogoutClient(c)
|
2012-12-15 23:34:20 +01:00
|
|
|
if !user.HasClients() {
|
2012-12-17 04:13:53 +01:00
|
|
|
cmd := &PartCommand{
|
2013-05-09 18:12:03 +02:00
|
|
|
BaseCommand: BaseCommand{c},
|
2012-12-15 23:34:20 +01:00
|
|
|
}
|
2012-12-17 04:13:53 +01:00
|
|
|
for channel := range user.channels {
|
2013-05-09 20:05:10 +02:00
|
|
|
channel.Commands() <- cmd
|
2012-12-15 23:34:20 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-09 18:12:03 +02:00
|
|
|
func (m *JoinCommand) HandleServer(s *Server) {
|
2012-12-15 23:34:20 +01:00
|
|
|
c := m.Client()
|
2012-12-17 04:13:53 +01:00
|
|
|
|
|
|
|
if c.user == nil {
|
2013-05-12 20:37:35 +02:00
|
|
|
c.Replies() <- ErrNoPrivileges(s)
|
2012-12-17 04:13:53 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2012-12-15 23:34:20 +01:00
|
|
|
if m.zero {
|
2012-12-17 04:13:53 +01:00
|
|
|
cmd := &PartCommand{
|
2013-05-09 18:12:03 +02:00
|
|
|
BaseCommand: BaseCommand{c},
|
2012-12-15 23:34:20 +01:00
|
|
|
}
|
|
|
|
for channel := range c.user.channels {
|
2013-05-09 20:05:10 +02:00
|
|
|
channel.Commands() <- cmd
|
2012-12-15 23:34:20 +01:00
|
|
|
}
|
2012-12-17 04:13:53 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
for name := range m.channels {
|
2013-05-09 20:05:10 +02:00
|
|
|
s.GetOrMakeChannel(name).Commands() <- m
|
2012-12-09 07:54:58 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-09 18:12:03 +02:00
|
|
|
func (m *PartCommand) HandleServer(s *Server) {
|
2013-05-12 20:37:35 +02:00
|
|
|
user := m.User()
|
2012-12-17 04:13:53 +01:00
|
|
|
|
|
|
|
if user == nil {
|
2013-05-12 20:37:35 +02:00
|
|
|
m.Client().Replies() <- ErrNoPrivileges(s)
|
2012-12-17 04:13:53 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2012-12-15 23:34:20 +01:00
|
|
|
for _, chname := range m.channels {
|
|
|
|
channel := s.channels[chname]
|
|
|
|
|
|
|
|
if channel == nil {
|
2013-05-09 20:05:10 +02:00
|
|
|
user.Replies() <- ErrNoSuchChannel(s, channel.name)
|
2012-12-15 23:34:20 +01:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2013-05-09 20:05:10 +02:00
|
|
|
channel.Commands() <- m
|
2012-12-09 07:54:58 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-09 18:12:03 +02:00
|
|
|
func (m *TopicCommand) HandleServer(s *Server) {
|
2013-05-12 20:37:35 +02:00
|
|
|
user := m.User()
|
2012-12-17 04:13:53 +01:00
|
|
|
|
2013-05-12 20:37:35 +02:00
|
|
|
// Hide all channels from logged-out clients.
|
2012-12-17 04:13:53 +01:00
|
|
|
if user == nil {
|
2013-05-12 20:37:35 +02:00
|
|
|
m.Client().Replies() <- ErrNoPrivileges(s)
|
2012-12-15 23:34:20 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2012-12-17 04:13:53 +01:00
|
|
|
channel := s.channels[m.channel]
|
|
|
|
if channel == nil {
|
2013-05-26 22:28:22 +02:00
|
|
|
m.Client().Replies() <- ErrNoSuchChannel(s, m.channel)
|
2012-12-15 23:34:20 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2013-05-09 20:05:10 +02:00
|
|
|
channel.Commands() <- m
|
2012-12-15 23:34:20 +01:00
|
|
|
}
|
|
|
|
|
2013-05-09 18:12:03 +02:00
|
|
|
func (m *PrivMsgCommand) HandleServer(s *Server) {
|
2012-12-17 04:13:53 +01:00
|
|
|
service := s.services[m.target]
|
|
|
|
if service != nil {
|
2013-05-09 20:05:10 +02:00
|
|
|
service.Commands() <- m
|
2012-12-17 04:13:53 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2013-05-12 20:37:35 +02:00
|
|
|
user := m.User()
|
|
|
|
// Hide all users from logged-out clients.
|
2012-12-17 04:13:53 +01:00
|
|
|
if user == nil {
|
2013-05-12 20:37:35 +02:00
|
|
|
m.Client().Replies() <- ErrNoPrivileges(s)
|
2012-12-17 04:13:53 +01:00
|
|
|
return
|
|
|
|
}
|
2012-12-15 23:34:20 +01:00
|
|
|
|
|
|
|
if m.TargetIsChannel() {
|
|
|
|
channel := s.channels[m.target]
|
|
|
|
if channel == nil {
|
2013-06-03 07:07:50 +02:00
|
|
|
m.Client().Replies() <- ErrNoSuchChannel(s, m.target)
|
2012-12-15 23:34:20 +01:00
|
|
|
return
|
2012-12-09 07:54:58 +01:00
|
|
|
}
|
2012-12-15 23:34:20 +01:00
|
|
|
|
2013-06-03 07:07:50 +02:00
|
|
|
channel.commands <- m
|
2012-12-15 23:34:20 +01:00
|
|
|
return
|
2012-12-09 07:54:58 +01:00
|
|
|
}
|
2012-12-15 23:34:20 +01:00
|
|
|
|
|
|
|
target := s.users[m.target]
|
2012-12-17 04:13:53 +01:00
|
|
|
if target == nil {
|
2013-06-03 07:07:50 +02:00
|
|
|
m.Client().Replies() <- ErrNoSuchNick(s, m.target)
|
2012-12-15 23:34:20 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2013-06-03 07:07:50 +02:00
|
|
|
target.commands <- m
|
2012-04-07 20:44:59 +02:00
|
|
|
}
|
2012-12-13 08:27:17 +01:00
|
|
|
|
2013-05-09 18:12:03 +02:00
|
|
|
func (m *ModeCommand) HandleServer(s *Server) {
|
2013-05-09 20:05:10 +02:00
|
|
|
m.Client().Replies() <- RplUModeIs(s, m.Client())
|
2012-12-13 08:27:17 +01:00
|
|
|
}
|