p
This commit is contained in:
parent
330bd96cdf
commit
c2551972a5
108
main.go
108
main.go
@ -236,12 +236,14 @@ func handleConn(conn net.Conn) {
|
|||||||
switch subcmd {
|
switch subcmd {
|
||||||
case "LS":
|
case "LS":
|
||||||
// explicitly tell we only support PLAIN SASL, as well as chathistory
|
// explicitly tell we only support PLAIN SASL, as well as chathistory
|
||||||
client.fwrite(":server CAP * LS :sasl=PLAIN sasl chathistory\r\n")
|
client.fwrite(":server CAP * LS :sasl=PLAIN sasl chathistory server-time message-tags draft/chathistory batch\r\n")
|
||||||
|
|
||||||
case "REQ":
|
case "REQ":
|
||||||
if len(msg.Params) >= 2 && strings.Contains(strings.ToLower(msg.Params[1]), "sasl") {
|
if len(msg.Params) >= 2 && strings.Contains(strings.ToLower(msg.Params[1]), "sasl") {
|
||||||
client.saslRequired = true
|
client.saslRequired = true
|
||||||
client.fwrite(":server CAP * ACK :sasl\r\n")
|
client.fwrite(":server CAP * ACK :sasl\r\n")
|
||||||
|
} else if len(msg.Params) >= 2 {
|
||||||
|
client.fwrite(":server CAP * ACK :%s\r\n", msg.Params[1])
|
||||||
} else {
|
} else {
|
||||||
client.fwrite(":server CAP * NAK :%s\r\n", msg.Params[1])
|
client.fwrite(":server CAP * NAK :%s\r\n", msg.Params[1])
|
||||||
}
|
}
|
||||||
@ -461,13 +463,17 @@ func handleConn(conn net.Conn) {
|
|||||||
}
|
}
|
||||||
// ircv3 stuff
|
// ircv3 stuff
|
||||||
case "CHATHISTORY":
|
case "CHATHISTORY":
|
||||||
if len(msg.Params) < 3 {
|
if len(msg.Params) < 4 {
|
||||||
client.fwrite(":server 461 %s CHATHISTORY :Not enough parameters\r\n", client.nick)
|
client.fwrite(":server 461 %s CHATHISTORY :Not enough parameters\r\n", client.nick)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
subcmd := strings.ToUpper(msg.Params[0])
|
subcmd := strings.ToUpper(msg.Params[0])
|
||||||
target := normalizeChannelName(msg.Params[1])
|
target := historyTargetName(msg.Params[1])
|
||||||
count, _ := strconv.Atoi(msg.Params[2])
|
count, convErr := strconv.Atoi(msg.Params[3])
|
||||||
|
if convErr != nil || count <= 0 {
|
||||||
|
client.fwrite(":server 461 %s CHATHISTORY :Invalid message count\r\n", client.nick)
|
||||||
|
continue
|
||||||
|
}
|
||||||
client.serveHistory(subcmd, target, count, msg.Params)
|
client.serveHistory(subcmd, target, count, msg.Params)
|
||||||
default:
|
default:
|
||||||
if client.nick != "" {
|
if client.nick != "" {
|
||||||
@ -478,6 +484,17 @@ func handleConn(conn net.Conn) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func historyTargetName(name string) string {
|
||||||
|
if strings.HasPrefix(name, "#") || strings.HasPrefix(name, "&") {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
if nicknameExists(name) {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Client) sendNames(ch *Channel) {
|
func (c *Client) sendNames(ch *Channel) {
|
||||||
ch.mu.Lock()
|
ch.mu.Lock()
|
||||||
defer ch.mu.Unlock()
|
defer ch.mu.Unlock()
|
||||||
@ -496,24 +513,29 @@ func (c *Client) sendNames(ch *Channel) {
|
|||||||
}
|
}
|
||||||
func (c *Client) serveHistory(subcmd, target string, count int, params []string) {
|
func (c *Client) serveHistory(subcmd, target string, count int, params []string) {
|
||||||
chMu.Lock()
|
chMu.Lock()
|
||||||
ch, ok := channels[target]
|
ch, isChannel := channels[target]
|
||||||
chMu.Unlock()
|
chMu.Unlock()
|
||||||
if !ok {
|
|
||||||
c.fwrite(":server 403 %s %s :No such channel\r\n", c.nick, target)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ch.mu.RLock()
|
var lockCh func()
|
||||||
defer ch.mu.RUnlock()
|
var unlockCh func()
|
||||||
|
if isChannel {
|
||||||
|
lockCh = ch.mu.RLock
|
||||||
|
unlockCh = ch.mu.RUnlock
|
||||||
|
lockCh()
|
||||||
|
defer unlockCh()
|
||||||
|
}
|
||||||
|
|
||||||
var msgs []HistoryMessage
|
var msgs []HistoryMessage
|
||||||
|
|
||||||
switch subcmd {
|
switch subcmd {
|
||||||
case "LATEST":
|
case "LATEST":
|
||||||
if count <= len(ch.history) {
|
if isChannel && count <= len(ch.history) {
|
||||||
msgs = ch.history[len(ch.history)-count:]
|
msgs = ch.history[len(ch.history)-count:]
|
||||||
} else {
|
} else {
|
||||||
extra := count - len(ch.history)
|
extra := count
|
||||||
|
if isChannel {
|
||||||
|
extra = count - len(ch.history)
|
||||||
|
}
|
||||||
rows, err := db.Query(`SELECT msgid, timestamp, prefix, command, msg
|
rows, err := db.Query(`SELECT msgid, timestamp, prefix, command, msg
|
||||||
FROM messages
|
FROM messages
|
||||||
WHERE channel=?
|
WHERE channel=?
|
||||||
@ -525,7 +547,14 @@ func (c *Client) serveHistory(subcmd, target string, count int, params []string)
|
|||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
dbMsgs := scanRows(rows, target)
|
dbMsgs := scanRows(rows, target)
|
||||||
msgs = append(dbMsgs, ch.history...)
|
if isChannel {
|
||||||
|
msgs = append(dbMsgs, ch.history...)
|
||||||
|
} else {
|
||||||
|
for i, j := 0, len(dbMsgs)-1; i < j; i, j = i+1, j-1 {
|
||||||
|
dbMsgs[i], dbMsgs[j] = dbMsgs[j], dbMsgs[i]
|
||||||
|
}
|
||||||
|
msgs = dbMsgs
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case "BEFORE":
|
case "BEFORE":
|
||||||
@ -570,7 +599,7 @@ func (c *Client) serveHistory(subcmd, target string, count int, params []string)
|
|||||||
c.fwrite(":server 461 %s CHATHISTORY :Invalid timestamp\r\n", c.nick)
|
c.fwrite(":server 461 %s CHATHISTORY :Invalid timestamp\r\n", c.nick)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
rows, err = db.Query(`SELECT msgid, nick, timestamp, channel, prefix, command, msg
|
rows, err = db.Query(`SELECT msgid, timestamp, prefix, command, msg
|
||||||
FROM messages
|
FROM messages
|
||||||
WHERE channel=? AND timestamp > ?
|
WHERE channel=? AND timestamp > ?
|
||||||
ORDER BY timestamp ASC LIMIT ?`,
|
ORDER BY timestamp ASC LIMIT ?`,
|
||||||
@ -662,7 +691,7 @@ func kickUser(sender *Client, channelName, targetNick, reason string) {
|
|||||||
ch.mu.Lock()
|
ch.mu.Lock()
|
||||||
defer ch.mu.Unlock()
|
defer ch.mu.Unlock()
|
||||||
|
|
||||||
if !ch.ops[sender.nick] {
|
if !ch.ops[sender.nick] && !globalOps[sender.nick] {
|
||||||
sender.fwrite(":server 482 %s %s :You're not channel operator\r\n", sender.nick, channelName)
|
sender.fwrite(":server 482 %s %s :You're not channel operator\r\n", sender.nick, channelName)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -696,7 +725,7 @@ func handleMode(sender *Client, channelName, mode, targetNick string) {
|
|||||||
ch.mu.Lock()
|
ch.mu.Lock()
|
||||||
defer ch.mu.Unlock()
|
defer ch.mu.Unlock()
|
||||||
|
|
||||||
if !ch.ops[sender.nick] {
|
if !ch.ops[sender.nick] && !globalOps[sender.nick] {
|
||||||
sender.fwrite(":server 482 %s %s :You're not channel operator\r\n", sender.nick, channelName)
|
sender.fwrite(":server 482 %s %s :You're not channel operator\r\n", sender.nick, channelName)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -787,6 +816,7 @@ func joinChannel(c *Client, name string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) partChannel(name string) {
|
func (c *Client) partChannel(name string) {
|
||||||
|
name = normalizeChannelName(name)
|
||||||
chMu.Lock()
|
chMu.Lock()
|
||||||
ch, ok := channels[name]
|
ch, ok := channels[name]
|
||||||
chMu.Unlock()
|
chMu.Unlock()
|
||||||
@ -801,7 +831,7 @@ func (c *Client) partChannel(name string) {
|
|||||||
fmt.Fprintf(member.conn, ":%s PART %s\r\n", c.nick, name)
|
fmt.Fprintf(member.conn, ":%s PART %s\r\n", c.nick, name)
|
||||||
}
|
}
|
||||||
ch.mu.Unlock()
|
ch.mu.Unlock()
|
||||||
// broadcastNames(ch)
|
broadcastNames(ch)
|
||||||
}
|
}
|
||||||
func addChannel(name string, description string) {
|
func addChannel(name string, description string) {
|
||||||
name = normalizeChannelName(name)
|
name = normalizeChannelName(name)
|
||||||
@ -818,11 +848,11 @@ func addChannel(name string, description string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
func sendMessage(sender *Client, target, text string) {
|
func sendMessage(sender *Client, target, text string) {
|
||||||
// TODO: handle when a user sends a message to a channel theyre not in
|
|
||||||
cliMu.Lock()
|
cliMu.Lock()
|
||||||
if c, ok := clients[target]; ok {
|
if c, ok := clients[target]; ok {
|
||||||
c.fwrite(":%s!%s@%s PRIVMSG %s :%s\r\n", sender.nick, sender.user, sender.host, target, text)
|
c.fwrite(":%s!%s@%s PRIVMSG %s :%s\r\n", sender.nick, sender.user, sender.host, target, text)
|
||||||
cliMu.Unlock()
|
cliMu.Unlock()
|
||||||
|
storeDirectMessage(sender, target, text)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cliMu.Unlock()
|
cliMu.Unlock()
|
||||||
@ -830,6 +860,7 @@ func sendMessage(sender *Client, target, text string) {
|
|||||||
ch, ok := channels[target]
|
ch, ok := channels[target]
|
||||||
chMu.Unlock()
|
chMu.Unlock()
|
||||||
if !ok {
|
if !ok {
|
||||||
|
sender.fwrite(":server 401 %s %s :No such nick/channel\r\n", sender.nick, target)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ch.mu.RLock()
|
ch.mu.RLock()
|
||||||
@ -851,7 +882,8 @@ func sendMessage(sender *Client, target, text string) {
|
|||||||
}
|
}
|
||||||
func (c *Client) checkRegistration() {
|
func (c *Client) checkRegistration() {
|
||||||
if c.nick != "" && c.user != "" && !c.registered {
|
if c.nick != "" && c.user != "" && !c.registered {
|
||||||
if c.saslRequired && !c.saslComplete {
|
if !c.saslComplete {
|
||||||
|
c.fwrite(":server 464 %s :You must authenticate before registering\r\n", c.nick)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.registered = true
|
c.registered = true
|
||||||
@ -896,7 +928,7 @@ func (c *Client) performWhois(nick string) {
|
|||||||
c.fwrite(":server 318 %s %s :End of WHOIS list\r\n", c.nick, nick)
|
c.fwrite(":server 318 %s %s :End of WHOIS list\r\n", c.nick, nick)
|
||||||
}
|
}
|
||||||
func (c *Client) sendUnregisteredNotice() {
|
func (c *Client) sendUnregisteredNotice() {
|
||||||
fmt.Fprintf(c.conn, ":server 451 * :You have not registered\r\n")
|
c.fwrite(":server 451 * :You have not registered\r\n")
|
||||||
}
|
}
|
||||||
func showWho(c *Client, name string) {
|
func showWho(c *Client, name string) {
|
||||||
name = normalizeChannelName(name)
|
name = normalizeChannelName(name)
|
||||||
@ -909,8 +941,7 @@ func showWho(c *Client, name string) {
|
|||||||
ch.mu.Lock()
|
ch.mu.Lock()
|
||||||
defer ch.mu.Unlock()
|
defer ch.mu.Unlock()
|
||||||
for _, member := range ch.members {
|
for _, member := range ch.members {
|
||||||
fmt.Fprintf(c.conn, ":server 352 %s %s %s server %s H :0 %s\r\n",
|
c.fwrite(":server 352 %s %s %s server %s H :0 %s\r\n", c.nick, name, member.host, member.nick, member.nick)
|
||||||
c.nick, name, member.host, member.nick, member.nick)
|
|
||||||
}
|
}
|
||||||
fmt.Fprintf(c.conn, ":server 315 %s %s :End of WHO list\r\n", c.nick, name)
|
fmt.Fprintf(c.conn, ":server 315 %s %s :End of WHO list\r\n", c.nick, name)
|
||||||
}
|
}
|
||||||
@ -931,9 +962,9 @@ func (c *Client) handleList() {
|
|||||||
}
|
}
|
||||||
func storeMessage(ch *Channel, sender *Client, target, text string) {
|
func storeMessage(ch *Channel, sender *Client, target, text string) {
|
||||||
msg := HistoryMessage{
|
msg := HistoryMessage{
|
||||||
MsgID: fmt.Sprintf("%d", time.Now().UnixNano()),
|
MsgID: fmt.Sprintf("%019d", time.Now().UnixNano()),
|
||||||
Time: time.Now(),
|
Time: time.Now(),
|
||||||
Prefix: fmt.Sprintf("%s!%s@%s", sender.nick, sender.user, "host"), // for privacy reasons
|
Prefix: fmt.Sprintf("%s!%s@%s", sender.nick, sender.user, sender.host),
|
||||||
Command: "PRIVMSG",
|
Command: "PRIVMSG",
|
||||||
Params: []string{target},
|
Params: []string{target},
|
||||||
Text: text,
|
Text: text,
|
||||||
@ -942,10 +973,26 @@ func storeMessage(ch *Channel, sender *Client, target, text string) {
|
|||||||
if len(ch.history) > 1000 {
|
if len(ch.history) > 1000 {
|
||||||
ch.history = ch.history[1:] // drop oldest
|
ch.history = ch.history[1:] // drop oldest
|
||||||
}
|
}
|
||||||
|
persistMessage(msg, sender.nick, target)
|
||||||
|
}
|
||||||
|
|
||||||
|
func storeDirectMessage(sender *Client, target, text string) {
|
||||||
|
msg := HistoryMessage{
|
||||||
|
MsgID: fmt.Sprintf("%019d", time.Now().UnixNano()),
|
||||||
|
Time: time.Now(),
|
||||||
|
Prefix: fmt.Sprintf("%s!%s@%s", sender.nick, sender.user, sender.host),
|
||||||
|
Command: "PRIVMSG",
|
||||||
|
Params: []string{target},
|
||||||
|
Text: text,
|
||||||
|
}
|
||||||
|
persistMessage(msg, sender.nick, target)
|
||||||
|
}
|
||||||
|
|
||||||
|
func persistMessage(msg HistoryMessage, nick, target string) {
|
||||||
_, err := db.Exec(`INSERT INTO messages(msgid, nick, timestamp, channel, prefix, command, msg)
|
_, err := db.Exec(`INSERT INTO messages(msgid, nick, timestamp, channel, prefix, command, msg)
|
||||||
VALUES(?, ?, ?, ?, ?, ?, ?)`,
|
VALUES(?, ?, ?, ?, ?, ?, ?)`,
|
||||||
msg.MsgID,
|
msg.MsgID,
|
||||||
sender.nick,
|
nick,
|
||||||
msg.Time.UnixNano(),
|
msg.Time.UnixNano(),
|
||||||
target,
|
target,
|
||||||
msg.Prefix,
|
msg.Prefix,
|
||||||
@ -953,7 +1000,7 @@ func storeMessage(ch *Channel, sender *Client, target, text string) {
|
|||||||
msg.Text,
|
msg.Text,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("storeMessage error: %v", err)
|
log.Printf("persistMessage error: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func nicknameExists(nick string) bool {
|
func nicknameExists(nick string) bool {
|
||||||
@ -969,13 +1016,12 @@ func (c *Client) cleanup() {
|
|||||||
for name, ch := range c.channels {
|
for name, ch := range c.channels {
|
||||||
ch.mu.Lock()
|
ch.mu.Lock()
|
||||||
delete(ch.members, c.nick)
|
delete(ch.members, c.nick)
|
||||||
|
ch.mu.Unlock()
|
||||||
for nick, member := range ch.members {
|
for nick, member := range ch.members {
|
||||||
member.fwrite(":%s PART %s\r\n", c.nick, name)
|
if nick != c.nick {
|
||||||
if member == c {
|
member.fwrite(":%s PART %s\r\n", c.nick, name)
|
||||||
delete(ch.members, nick)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ch.mu.Unlock()
|
|
||||||
delete(c.channels, name)
|
delete(c.channels, name)
|
||||||
}
|
}
|
||||||
cliMu.RLock()
|
cliMu.RLock()
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user