Fixes by Gunnar

This commit is contained in:
Gunnar Ruthenberg 2018-03-04 17:20:25 +00:00 committed by Björn Busse
parent 2d71da2234
commit a9a5b17ce4
3 changed files with 122 additions and 63 deletions

View File

@ -49,6 +49,7 @@ type Client struct {
sendTimestamp time.Time sendTimestamp time.Time
outBuf chan *string outBuf chan *string
alive bool alive bool
quitMsg *string
sync.Mutex sync.Mutex
} }
@ -67,13 +68,19 @@ func (c *Client) String() string {
return *c.nickname + "!" + *c.username + "@" + c.Host() return *c.nickname + "!" + *c.username + "@" + c.Host()
} }
func (c *Client) Match(other string) bool {
return strings.ToLower(*c.nickname) == strings.ToLower(other)
}
func NewClient(conn *proxyproto.Conn) *Client { func NewClient(conn *proxyproto.Conn) *Client {
nickname := "*" nickname := "*"
username := "" username := ""
realname := ""
c := Client{ c := Client{
conn: conn, conn: conn,
nickname: &nickname, nickname: &nickname,
username: &username, username: &username,
realname: &realname,
recvTimestamp: time.Now(), recvTimestamp: time.Now(),
sendTimestamp: time.Now(), sendTimestamp: time.Now(),
alive: true, alive: true,
@ -88,9 +95,10 @@ func (c *Client) SetDead() {
c.alive = false c.alive = false
} }
func (c *Client) Close() { func (c *Client) Close(text string) {
c.Lock() c.Lock()
if c.alive { if c.alive {
c.quitMsg = &text
c.SetDead() c.SetDead()
} }
c.Unlock() c.Unlock()
@ -127,8 +135,8 @@ func (c *Client) Processor(sink chan ClientEvent) {
prev -= (i + 2) prev -= (i + 2)
goto CheckMore goto CheckMore
} }
c.Close() c.Close("error")
sink <- ClientEvent{c, EventDel, ""} sink <- ClientEvent{c, EventDel, *c.quitMsg}
} }
func (c *Client) MsgSender() { func (c *Client) MsgSender() {

155
daemon.go
View File

@ -38,7 +38,7 @@ const (
) )
var ( var (
RENickname = regexp.MustCompile("^[a-zA-Z0-9-]{1,24}$") RENickname = regexp.MustCompile("^[^\\x00\\x0D\\x0A\\x20\\x3A]{1,64}$") // any octet except NUL, CR, LF, " " and ":"
clients map[*Client]struct{} = make(map[*Client]struct{}) clients map[*Client]struct{} = make(map[*Client]struct{})
clientsM sync.RWMutex clientsM sync.RWMutex
@ -48,6 +48,18 @@ var (
roomSinks map[*Room]chan ClientEvent = make(map[*Room]chan ClientEvent) roomSinks map[*Room]chan ClientEvent = make(map[*Room]chan ClientEvent)
) )
func GetRoom(name string) (r *Room, found bool) {
var room string
if strings.HasPrefix(name, "#") {
room = strings.ToLower(name)
} else {
room = "#" + strings.ToLower(name)
}
r, found = rooms[room]
return r, found
}
func SendLusers(client *Client) { func SendLusers(client *Client) {
lusers := 0 lusers := 0
clientsM.RLock() clientsM.RLock()
@ -86,11 +98,9 @@ func SendWhois(client *Client, nicknames []string) {
var room *Room var room *Room
var subscriber *Client var subscriber *Client
for _, nickname := range nicknames { for _, nickname := range nicknames {
nickname = strings.ToLower(nickname)
clientsM.RLock() clientsM.RLock()
for c = range clients { for c = range clients {
if strings.ToLower(*c.nickname) == nickname { if c.Match(nickname) {
clientsM.RUnlock()
goto Found goto Found
} }
} }
@ -112,7 +122,7 @@ func SendWhois(client *Client, nicknames []string) {
roomsM.RLock() roomsM.RLock()
for _, room = range rooms { for _, room = range rooms {
for subscriber = range room.members { for subscriber = range room.members {
if *subscriber.nickname == nickname { if subscriber.Match(nickname) {
subscriptions = append(subscriptions, *room.name) subscriptions = append(subscriptions, *room.name)
} }
} }
@ -121,6 +131,7 @@ func SendWhois(client *Client, nicknames []string) {
sort.Strings(subscriptions) sort.Strings(subscriptions)
client.ReplyNicknamed("319", *c.nickname, strings.Join(subscriptions, " ")) client.ReplyNicknamed("319", *c.nickname, strings.Join(subscriptions, " "))
client.ReplyNicknamed("318", *c.nickname, "End of /WHOIS list") client.ReplyNicknamed("318", *c.nickname, "End of /WHOIS list")
clientsM.RUnlock()
} }
} }
@ -145,7 +156,7 @@ func SendList(client *Client, cols []string) {
if room, found = rooms[r]; found { if room, found = rooms[r]; found {
client.ReplyNicknamed( client.ReplyNicknamed(
"322", "322",
r, *room.name,
fmt.Sprintf("%d", len(room.members)), fmt.Sprintf("%d", len(room.members)),
*room.topic, *room.topic,
) )
@ -155,6 +166,54 @@ func SendList(client *Client, cols []string) {
client.ReplyNicknamed("323", "End of /LIST") client.ReplyNicknamed("323", "End of /LIST")
} }
func ClientNick(client *Client, cols []string) {
if len(cols) == 1 || len(cols[1]) < 1 {
client.ReplyParts("431", "No nickname given")
return
}
nickname := cols[1]
// Compatibility with some clients prepending colons to nickname
nickname = strings.TrimPrefix(nickname, ":")
rename := false
clientsM.RLock()
for existingClient := range clients {
if existingClient == client {
rename = true
} else if existingClient.Match(nickname) {
clientsM.RUnlock()
client.ReplyParts("433", "*", nickname, "Nickname is already in use")
return
}
}
clientsM.RUnlock()
if !RENickname.MatchString(nickname) {
client.ReplyParts("432", "*", cols[1], "Erroneous nickname")
return
}
if rename {
// find all rooms the client has subscribed,
// then gather all clients in those rooms
cs := make(map[*Client]struct{})
clientsM.RLock() // first clients, then rooms,
roomsM.RLock() // to avoid deadlock with SendWhois
for _, r := range rooms {
if _, subscribed := r.members[client]; subscribed {
for c := range r.members {
cs[c] = struct{}{}
}
}
}
// then notify those clients of the nick change
message := ":" + client.String() + " NICK " + nickname
for c := range cs {
c.Msg(message)
}
roomsM.RUnlock()
clientsM.RUnlock()
}
client.nickname = &nickname
}
// Unregistered client workflow processor. Unregistered client: // Unregistered client workflow processor. Unregistered client:
// * is not PINGed // * is not PINGed
// * only QUIT, NICK and USER commands are processed // * only QUIT, NICK and USER commands are processed
@ -170,28 +229,7 @@ func ClientRegister(client *Client, cmd string, cols []string) {
password := strings.TrimPrefix(cols[1], ":") password := strings.TrimPrefix(cols[1], ":")
client.password = &password client.password = &password
case "NICK": case "NICK":
if len(cols) == 1 || len(cols[1]) < 1 { ClientNick(client, cols)
client.ReplyParts("431", "No nickname given")
return
}
nickname := cols[1]
// Compatibility with some clients prepending colons to nickname
nickname = strings.TrimPrefix(nickname, ":")
nickname = strings.ToLower(nickname)
clientsM.RLock()
for existingClient := range clients {
if *existingClient.nickname == nickname {
clientsM.RUnlock()
client.ReplyParts("433", "*", nickname, "Nickname is already in use")
return
}
}
clientsM.RUnlock()
if !RENickname.MatchString(nickname) {
client.ReplyParts("432", "*", cols[1], "Erroneous nickname")
return
}
client.nickname = &nickname
case "USER": case "USER":
if len(cols) == 1 { if len(cols) == 1 {
client.ReplyNotEnoughParameters("USER") client.ReplyNotEnoughParameters("USER")
@ -210,7 +248,7 @@ func ClientRegister(client *Client, cmd string, cols []string) {
if passwords != nil && *passwords != "" { if passwords != nil && *passwords != "" {
if client.password == nil { if client.password == nil {
client.ReplyParts("462", "You may not register") client.ReplyParts("462", "You may not register")
client.Close() client.Close("462")
return return
} }
contents, err := ioutil.ReadFile(*passwords) contents, err := ioutil.ReadFile(*passwords)
@ -224,7 +262,7 @@ func ClientRegister(client *Client, cmd string, cols []string) {
} }
if lp := strings.Split(entry, ":"); lp[0] == *client.nickname && lp[1] != *client.password { if lp := strings.Split(entry, ":"); lp[0] == *client.nickname && lp[1] != *client.password {
client.ReplyParts("462", "You may not register") client.ReplyParts("462", "You may not register")
client.Close() client.Close("462")
return return
} }
} }
@ -246,7 +284,7 @@ func RoomRegister(name string) (*Room, chan ClientEvent) {
roomNew := NewRoom(name) roomNew := NewRoom(name)
roomSink := make(chan ClientEvent) roomSink := make(chan ClientEvent)
roomsM.Lock() roomsM.Lock()
rooms[name] = roomNew rooms[strings.ToLower(name)] = roomNew
roomSinks[roomNew] = roomSink roomSinks[roomNew] = roomSink
roomsM.Unlock() roomsM.Unlock()
go roomNew.Processor(roomSink) go roomNew.Processor(roomSink)
@ -279,7 +317,7 @@ func HandlerJoin(client *Client, cmd string) {
} }
roomsM.RLock() roomsM.RLock()
for roomExisting, roomSink = range roomSinks { for roomExisting, roomSink = range roomSinks {
if room == *roomExisting.name { if roomExisting.Match(room) {
roomsM.RUnlock() roomsM.RUnlock()
if (*roomExisting.key != "") && (*roomExisting.key != key) { if (*roomExisting.key != "") && (*roomExisting.key != key) {
goto Denied goto Denied
@ -320,7 +358,7 @@ func Processor(events chan ClientEvent, finished chan struct{}) {
for c := range clients { for c := range clients {
if c.recvTimestamp.Add(PingTimeout).Before(now) { if c.recvTimestamp.Add(PingTimeout).Before(now) {
log.Println(c, "ping timeout") log.Println(c, "ping timeout")
c.Close() c.Close("ping timeout")
continue continue
} }
if c.sendTimestamp.Add(PingThreshold).Before(now) { if c.sendTimestamp.Add(PingThreshold).Before(now) {
@ -329,7 +367,7 @@ func Processor(events chan ClientEvent, finished chan struct{}) {
c.sendTimestamp = time.Now() c.sendTimestamp = time.Now()
} else { } else {
log.Println(c, "ping timeout") log.Println(c, "ping timeout")
c.Close() c.Close("ping timeout")
} }
} }
} }
@ -374,7 +412,13 @@ func Processor(events chan ClientEvent, finished chan struct{}) {
} }
if cmd == "QUIT" { if cmd == "QUIT" {
log.Println(client, "quit") log.Println(client, "quit")
client.Close() var quitMsg string
if len(cols) >= 2 {
quitMsg = strings.TrimPrefix(cols[1], ":")
} else {
quitMsg = *client.nickname
}
client.Close(quitMsg)
continue continue
} }
if !client.registered { if !client.registered {
@ -400,6 +444,8 @@ func Processor(events chan ClientEvent, finished chan struct{}) {
continue continue
} }
HandlerJoin(client, cols[1]) HandlerJoin(client, cols[1])
case "NICK":
ClientNick(client, cols)
case "LIST": case "LIST":
SendList(client, cols) SendList(client, cols)
case "LUSERS": case "LUSERS":
@ -410,7 +456,7 @@ func Processor(events chan ClientEvent, finished chan struct{}) {
continue continue
} }
cols = strings.SplitN(cols[1], " ", 2) cols = strings.SplitN(cols[1], " ", 2)
if cols[0] == *client.username { if strings.ToLower(cols[0]) == strings.ToLower(*client.username) {
if len(cols) == 1 { if len(cols) == 1 {
client.Msg("221 " + *client.nickname + " +") client.Msg("221 " + *client.nickname + " +")
} else { } else {
@ -420,7 +466,7 @@ func Processor(events chan ClientEvent, finished chan struct{}) {
} }
room := cols[0] room := cols[0]
roomsM.RLock() roomsM.RLock()
r, found := rooms[room] r, found := GetRoom(room)
if !found { if !found {
client.ReplyNoChannel(room) client.ReplyNoChannel(room)
roomsM.RUnlock() roomsM.RUnlock()
@ -439,13 +485,18 @@ func Processor(events chan ClientEvent, finished chan struct{}) {
client.ReplyNotEnoughParameters("PART") client.ReplyNotEnoughParameters("PART")
continue continue
} }
rs := strings.Split(cols[1], " ")[0] rs := strings.SplitN(cols[1], " ", 2)
roomsM.RLock() roomsM.RLock()
for _, room := range strings.Split(rs, ",") { for _, room := range strings.Split(rs[0], ",") {
if r, found := rooms[room]; found { if r, found := GetRoom(room); found {
roomSinks[r] <- ClientEvent{client, EventDel, ""} var partMsg string
if len(rs) >= 2 {
partMsg = strings.TrimPrefix(rs[1], ":")
} else {
partMsg = *client.nickname
}
roomSinks[r] <- ClientEvent{client, EventDel, partMsg}
} else { } else {
roomsM.RUnlock()
client.ReplyNoChannel(room) client.ReplyNoChannel(room)
continue continue
} }
@ -470,10 +521,10 @@ func Processor(events chan ClientEvent, finished chan struct{}) {
continue continue
} }
msg := "" msg := ""
target := strings.ToLower(cols[0]) target := cols[0]
clientsM.RLock() clientsM.RLock()
for c := range clients { for c := range clients {
if *c.nickname == target { if c.Match(target) {
msg = fmt.Sprintf(":%s %s %s %s", client, cmd, *c.nickname, cols[1]) msg = fmt.Sprintf(":%s %s %s %s", client, cmd, *c.nickname, cols[1])
c.Msg(msg) c.Msg(msg)
if c.away != nil { if c.away != nil {
@ -487,7 +538,7 @@ func Processor(events chan ClientEvent, finished chan struct{}) {
continue continue
} }
roomsM.RLock() roomsM.RLock()
if r, found := rooms[target]; found { if r, found := rooms[strings.ToLower(target)]; found {
roomSinks[r] <- ClientEvent{ roomSinks[r] <- ClientEvent{
client, client,
EventMsg, EventMsg,
@ -504,7 +555,7 @@ func Processor(events chan ClientEvent, finished chan struct{}) {
} }
cols = strings.SplitN(cols[1], " ", 2) cols = strings.SplitN(cols[1], " ", 2)
roomsM.RLock() roomsM.RLock()
r, found := rooms[cols[0]] r, found := GetRoom(cols[0])
roomsM.RUnlock() roomsM.RUnlock()
if !found { if !found {
client.ReplyNoChannel(cols[0]) client.ReplyNoChannel(cols[0])
@ -526,7 +577,7 @@ func Processor(events chan ClientEvent, finished chan struct{}) {
} }
room := strings.Split(cols[1], " ")[0] room := strings.Split(cols[1], " ")[0]
roomsM.RLock() roomsM.RLock()
if r, found := rooms[room]; found { if r, found := GetRoom(room); found {
roomSinks[r] <- ClientEvent{client, EventWho, ""} roomSinks[r] <- ClientEvent{client, EventWho, ""}
} else { } else {
client.ReplyNoChannel(room) client.ReplyNoChannel(room)
@ -545,18 +596,16 @@ func Processor(events chan ClientEvent, finished chan struct{}) {
client.ReplyNotEnoughParameters("ISON") client.ReplyNotEnoughParameters("ISON")
continue continue
} }
nicksKnown := make(map[string]struct{})
clientsM.RLock() clientsM.RLock()
for c := range clients {
nicksKnown[*c.nickname] = struct{}{}
}
clientsM.RUnlock()
var nicksExists []string var nicksExists []string
for _, nickname := range strings.Split(cols[1], " ") { for _, nickname := range strings.Split(cols[1], " ") {
if _, exists := nicksKnown[nickname]; exists { for c := range clients {
if c.Match(nickname) {
nicksExists = append(nicksExists, nickname) nicksExists = append(nicksExists, nickname)
} }
} }
}
clientsM.RUnlock()
client.ReplyNicknamed("303", strings.Join(nicksExists, " ")) client.ReplyNicknamed("303", strings.Join(nicksExists, " "))
case "VERSION": case "VERSION":
var debug string var debug string

12
room.go
View File

@ -63,6 +63,10 @@ func NewRoom(name string) *Room {
} }
} }
func (room *Room) Match(other string) bool {
return strings.ToLower(*room.name) == strings.ToLower(other)
}
func (room *Room) SendTopic(client *Client) { func (room *Room) SendTopic(client *Client) {
room.RLock() room.RLock()
if *room.topic == "" { if *room.topic == "" {
@ -125,15 +129,13 @@ func (room *Room) Processor(events <-chan ClientEvent) {
room.RUnlock() room.RUnlock()
continue continue
} }
msg := fmt.Sprintf(":%s PART %s :%s", client, room.String(), event.text)
room.Broadcast(msg)
logSink <- LogEvent{room.String(), *client.nickname, "left", true}
room.RUnlock() room.RUnlock()
room.Lock() room.Lock()
delete(room.members, client) delete(room.members, client)
room.Unlock() room.Unlock()
room.RLock()
msg := fmt.Sprintf(":%s PART %s :%s", client, room.String(), *client.nickname)
room.Broadcast(msg)
logSink <- LogEvent{room.String(), *client.nickname, "left", true}
room.RUnlock()
case EventTopic: case EventTopic:
room.RLock() room.RLock()
if _, subscribed := room.members[client]; !subscribed { if _, subscribed := room.members[client]; !subscribed {