major: fixed bug in joinchannel, added INVITE command

This commit is contained in:
meera 2026-07-18 17:17:51 +03:00
parent 0898143876
commit c859f99b1e
3 changed files with 95 additions and 24 deletions

5
go.mod
View File

@ -2,4 +2,7 @@ module ircserve
go 1.26.3
require github.com/ergochat/irc-go v0.6.0
require (
github.com/ergochat/irc-go v0.6.0
github.com/mattn/go-sqlite3 v1.14.48
)

2
go.sum
View File

@ -1,2 +1,4 @@
github.com/ergochat/irc-go v0.6.0 h1:Y0AGV76aeihJfCtLaQh+OyJKFiKGrYC0VTkeMZ6XW28=
github.com/ergochat/irc-go v0.6.0/go.mod h1:2vi7KNpIPWnReB5hmLpl92eMywQvuIeIIGdt/FQCph0=
github.com/mattn/go-sqlite3 v1.14.48 h1:7XHIgl0a8HwOaiK4E47ozLkST78rR9+OtNGx27D/TFs=
github.com/mattn/go-sqlite3 v1.14.48/go.mod h1:6JTjA44L93a0QCyJef5YvlPoKXntQPjzWv5gtm9sB6w=

112
main.go
View File

@ -171,19 +171,25 @@ func handleConn(conn net.Conn) {
client.fwrite(":server 433 * %s :Nickname is already in use\r\n", newNick)
} else {
oldNick := client.nick
client.nick = newNick
cliMu.Lock()
if oldNick != "" {
// nick change
cliMu.Lock()
delete(clients, oldNick)
clients[newNick] = client
cliMu.Unlock()
for _, ch := range client.channels {
ch.mu.Lock()
}
clients[newNick] = client
cliMu.Unlock()
for _, ch := range client.channels {
ch.mu.Lock()
if oldNick != "" {
delete(ch.members, oldNick)
ch.members[newNick] = client
ch.mu.Unlock()
} else {
delete(ch.members, "")
}
client.nick = newNick
ch.members[newNick] = client
ch.mu.Unlock()
}
if oldNick != "" {
for _, ch := range client.channels {
broadcastNames(ch)
}
@ -192,15 +198,12 @@ func handleConn(conn net.Conn) {
other.fwrite(":%s NICK %s\r\n", oldNick, newNick)
}
}
} else {
client.nick = newNick
cliMu.Lock()
clients[newNick] = client
cliMu.Unlock()
}
client.checkRegistration()
}
}
case "CAP":
if len(msg.Params) < 1 {
client.fwrite(":server 461 %s CAP :Not enough parameters\r\n", client.nick)
@ -210,7 +213,7 @@ func handleConn(conn net.Conn) {
switch subcmd {
case "LS":
// explicitly tell we only support PLAIN SASL, as well as chathistory
client.fwrite(":server CAP * LS :sasl=PLAIN chathistory\r\n")
client.fwrite(":server CAP * LS :sasl=PLAIN sasl chathistory\r\n")
case "REQ":
if len(msg.Params) >= 2 && strings.Contains(strings.ToLower(msg.Params[1]), "sasl") {
@ -335,6 +338,44 @@ func handleConn(conn net.Conn) {
text := msg.Params[1]
sendNotice(client, target, text)
}
case "INVITE":
if !client.registered {
client.sendUnregisteredNotice()
continue
}
if len(msg.Params) < 2 {
client.fwrite(":server 461 %s INVITE :Not enough parameters\r\n", client.nick)
continue
}
targetNick := msg.Params[0]
channelName := normalizeChannelName(msg.Params[1])
cliMu.RLock()
target, ok := clients[targetNick]
cliMu.RUnlock()
if !ok {
client.fwrite(":server 401 %s %s :No such nick\r\n", client.nick, targetNick)
continue
}
chMu.Lock()
ch, ok := channels[channelName]
chMu.Unlock()
if !ok {
client.fwrite(":server 403 %s %s :No such channel\r\n", client.nick, channelName)
continue
}
ch.mu.RLock()
_, inChannel := ch.members[client.nick]
ch.mu.RUnlock()
if !inChannel {
client.fwrite(":server 442 %s %s :You're not on that channel\r\n", client.nick, channelName)
continue
}
client.fwrite(":server 341 %s %s %s\r\n", client.nick, targetNick, channelName)
target.fwrite(":%s INVITE %s :%s\r\n", client.nick, targetNick, channelName)
case "QUIT":
if !client.registered {
client.sendUnregisteredNotice()
@ -394,6 +435,7 @@ func handleConn(conn net.Conn) {
} else {
fmt.Fprintf(client.conn, ":server 461 %s NAMES :Not enough parameters\r\n", client.nick)
}
// ircv3 stuff
case "CHATHISTORY":
if len(msg.Params) < 3 {
client.fwrite(":server 461 %s CHATHISTORY :Not enough parameters\r\n", client.nick)
@ -624,9 +666,10 @@ func handleMode(sender *Client, channelName, mode, targetNick string) {
sender.fwrite(":server 501 %s :Unknown MODE flag\r\n", sender.nick)
}
}
func joinChannel(c *Client, name string) {
name = normalizeChannelName(name)
// get or create channel
chMu.Lock()
ch, ok := channels[name]
if !ok {
@ -634,13 +677,9 @@ func joinChannel(c *Client, name string) {
name: name,
members: make(map[string]*Client),
ops: make(map[string]bool),
history: nil,
description: "none",
}
channels[name] = ch
if globalOps[c.nick] {
ch.ops[c.nick] = true
}
}
if globalOps[c.nick] {
ch.ops[c.nick] = true
@ -648,20 +687,47 @@ func joinChannel(c *Client, name string) {
chMu.Unlock()
ch.mu.Lock()
// reject duplicate join
if _, exists := ch.members[c.nick]; exists {
ch.mu.Unlock()
c.fwrite(":server 443 %s %s %s :is already on channel\r\n",
c.nick, c.nick, name)
return
}
// clean up stale empty nick
delete(ch.members, "")
// add new member
ch.members[c.nick] = c
c.channels[name] = ch
ch.mu.Unlock()
// echo JOIN to client
fmt.Fprintf(c.conn, ":%s JOIN %s\r\n", c.nick, name)
// broadcast JOIN to others
for _, member := range ch.members {
fmt.Fprintf(member.conn, ":%s JOIN %s\r\n", c.nick, name)
if member != c {
fmt.Fprintf(member.conn, ":%s JOIN %s\r\n", c.nick, name)
}
}
// topic
if ch.description != "" {
c.fwrite(":server 332 %s %s :%s\r\n", c.nick, name, ch.description)
} else {
c.fwrite(":server 331 %s %s :No topic is set\r\n", c.nick, name)
}
// names list
c.sendNames(ch)
// MODE if op
if ch.ops[c.nick] {
for _, member := range ch.members {
member.fwrite(":server MODE %s +o %s\r\n", name, c.nick)
}
}
c.sendNames(ch)
}
func (c *Client) partChannel(name string) {
chMu.Lock()
ch, ok := channels[name]
@ -809,7 +875,7 @@ func storeMessage(ch *Channel, sender *Client, target, text string) {
msg := HistoryMessage{
MsgID: fmt.Sprintf("%d", time.Now().UnixNano()),
Time: time.Now(),
Prefix: fmt.Sprintf("%s!%s@%s", sender.nick, sender.user, sender.host),
Prefix: fmt.Sprintf("%s!%s@%s", sender.nick, sender.user, "host"), // for privacy reasons
Command: "PRIVMSG",
Params: []string{target},
Text: text,