3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-11-25 13:29:27 +01:00

Merge pull request #321 from slingamn/misc.2

eight small changes
This commit is contained in:
Daniel Oaks 2018-12-31 13:12:34 +10:00 committed by GitHub
commit 847922e53d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 120 additions and 74 deletions

View File

@ -346,6 +346,9 @@ func (channel *Channel) IsEmpty() bool {
// Join joins the given client to this channel (if they can be joined). // Join joins the given client to this channel (if they can be joined).
func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *ResponseBuffer) { func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *ResponseBuffer) {
account := client.Account()
nickMaskCasefolded := client.NickMaskCasefolded()
channel.stateMutex.RLock() channel.stateMutex.RLock()
chname := channel.name chname := channel.name
chcfname := channel.nameCasefolded chcfname := channel.nameCasefolded
@ -354,6 +357,7 @@ func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *Resp
limit := channel.userLimit limit := channel.userLimit
chcount := len(channel.members) chcount := len(channel.members)
_, alreadyJoined := channel.members[client] _, alreadyJoined := channel.members[client]
persistentMode := channel.accountToUMode[account]
channel.stateMutex.RUnlock() channel.stateMutex.RUnlock()
if alreadyJoined { if alreadyJoined {
@ -361,9 +365,9 @@ func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *Resp
return return
} }
account := client.Account() // the founder can always join (even if they disabled auto +q on join);
nickMaskCasefolded := client.NickMaskCasefolded() // anyone who automatically receives halfop or higher can always join
hasPrivs := isSajoin || (founder != "" && founder == account) hasPrivs := isSajoin || (founder != "" && founder == account) || (persistentMode != 0 && persistentMode != modes.Voice)
if !hasPrivs && limit != 0 && chcount >= limit { if !hasPrivs && limit != 0 && chcount >= limit {
rb.Add(nil, client.server.name, ERR_CHANNELISFULL, chname, fmt.Sprintf(client.t("Cannot join channel (+%s)"), "l")) rb.Add(nil, client.server.name, ERR_CHANNELISFULL, chname, fmt.Sprintf(client.t("Cannot join channel (+%s)"), "l"))
@ -404,7 +408,7 @@ func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *Resp
if newChannel { if newChannel {
givenMode = modes.ChannelOperator givenMode = modes.ChannelOperator
} else { } else {
givenMode = channel.accountToUMode[account] givenMode = persistentMode
} }
if givenMode != 0 { if givenMode != 0 {
channel.members[client].SetMode(givenMode, true) channel.members[client].SetMode(givenMode, true)
@ -803,6 +807,8 @@ func (channel *Channel) sendSplitMessage(msgid, cmd string, histType history.Ite
nickmask := client.NickMaskString() nickmask := client.NickMaskString()
account := client.AccountName() account := client.AccountName()
now := time.Now().UTC()
for _, member := range channel.Members() { for _, member := range channel.Members() {
if minPrefix != nil && !channel.ClientIsAtLeast(member, minPrefixMode) { if minPrefix != nil && !channel.ClientIsAtLeast(member, minPrefixMode) {
// STATUSMSG // STATUSMSG
@ -817,11 +823,10 @@ func (channel *Channel) sendSplitMessage(msgid, cmd string, histType history.Ite
tagsToUse = clientOnlyTags tagsToUse = clientOnlyTags
} }
// TODO(slingamn) evaluate an optimization where we reuse `nickmask` and `account`
if message == nil { if message == nil {
member.SendFromClient(msgid, client, tagsToUse, cmd, channel.name) member.sendFromClientInternal(false, now, msgid, nickmask, account, tagsToUse, cmd, channel.name)
} else { } else {
member.SendSplitMsgFromClient(msgid, client, tagsToUse, cmd, channel.name, *message) member.sendSplitMsgFromClientInternal(false, now, msgid, nickmask, account, tagsToUse, cmd, channel.name, *message)
} }
} }
@ -831,6 +836,7 @@ func (channel *Channel) sendSplitMessage(msgid, cmd string, histType history.Ite
Message: *message, Message: *message,
Nick: nickmask, Nick: nickmask,
AccountName: account, AccountName: account,
Time: now,
}) })
} }

View File

@ -28,7 +28,7 @@ import (
const ( const (
// IdentTimeoutSeconds is how many seconds before our ident (username) check times out. // IdentTimeoutSeconds is how many seconds before our ident (username) check times out.
IdentTimeoutSeconds = 1.5 IdentTimeoutSeconds = 1.5
IRCv3TimestampFormat = "2006-01-02T15:04:05.999Z" IRCv3TimestampFormat = "2006-01-02T15:04:05.000Z"
) )
var ( var (
@ -332,12 +332,6 @@ func (client *Client) Active() {
client.atime = time.Now() client.atime = time.Now()
} }
// Touch marks the client as alive (as it it has a connection to us and we
// can receive messages from it).
func (client *Client) Touch() {
client.idletimer.Touch()
}
// Ping sends the client a PING message. // Ping sends the client a PING message.
func (client *Client) Ping() { func (client *Client) Ping() {
client.Send(nil, "", "PING", client.nick) client.Send(nil, "", "PING", client.nick)

View File

@ -189,7 +189,7 @@ func (clients *ClientManager) FindAll(userhost string) (set ClientSet) {
clients.RLock() clients.RLock()
defer clients.RUnlock() defer clients.RUnlock()
for _, client := range clients.byNick { for _, client := range clients.byNick {
if matcher.Match(client.nickMaskCasefolded) { if matcher.Match(client.NickMaskCasefolded()) {
set.Add(client) set.Add(client)
} }
} }
@ -209,7 +209,7 @@ func (clients *ClientManager) Find(userhost string) *Client {
clients.RLock() clients.RLock()
defer clients.RUnlock() defer clients.RUnlock()
for _, client := range clients.byNick { for _, client := range clients.byNick {
if matcher.Match(client.nickMaskCasefolded) { if matcher.Match(client.NickMaskCasefolded()) {
matchedClient = client matchedClient = client
break break
} }

View File

@ -12,13 +12,12 @@ import (
// Command represents a command accepted from a client. // Command represents a command accepted from a client.
type Command struct { type Command struct {
handler func(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool handler func(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool
oper bool oper bool
usablePreReg bool usablePreReg bool
leaveClientActive bool // if true, leaves the client active time alone. reversed because we can't default a struct element to True leaveClientIdle bool // if true, leaves the client active time alone
leaveClientIdle bool minParams int
minParams int capabs []string
capabs []string
} }
// Run runs this command with the given client/message. // Run runs this command with the given client/message.
@ -54,11 +53,10 @@ func (cmd *Command) Run(server *Server, client *Client, msg ircmsg.IrcMessage) b
server.tryRegister(client) server.tryRegister(client)
} }
if !cmd.leaveClientIdle { // most servers do this only for PING/PONG, but we'll do it for any command:
client.Touch() client.idletimer.Touch()
}
if !cmd.leaveClientActive { if !cmd.leaveClientIdle {
client.Active() client.Active()
} }
@ -118,8 +116,9 @@ func init() {
minParams: 2, minParams: 2,
}, },
"ISON": { "ISON": {
handler: isonHandler, handler: isonHandler,
minParams: 1, minParams: 1,
leaveClientIdle: true,
}, },
"JOIN": { "JOIN": {
handler: joinHandler, handler: joinHandler,
@ -200,16 +199,16 @@ func init() {
minParams: 1, minParams: 1,
}, },
"PING": { "PING": {
handler: pingHandler, handler: pingHandler,
usablePreReg: true, usablePreReg: true,
minParams: 1, minParams: 1,
leaveClientActive: true, leaveClientIdle: true,
}, },
"PONG": { "PONG": {
handler: pongHandler, handler: pongHandler,
usablePreReg: true, usablePreReg: true,
minParams: 1, minParams: 1,
leaveClientActive: true, leaveClientIdle: true,
}, },
"PRIVMSG": { "PRIVMSG": {
handler: privmsgHandler, handler: privmsgHandler,

View File

@ -2478,16 +2478,25 @@ func whoisHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Res
return false return false
} }
handleService := func(nick string) bool {
cfnick, _ := CasefoldName(nick)
service, ok := OragonoServices[cfnick]
if !ok {
return false
}
clientNick := client.Nick()
rb.Add(nil, client.server.name, RPL_WHOISUSER, clientNick, service.Name, service.Name, "localhost", "*", fmt.Sprintf(client.t("Network service, for more info /msg %s HELP"), service.Name))
// hehe
if client.HasMode(modes.TLS) {
rb.Add(nil, client.server.name, RPL_WHOISSECURE, clientNick, service.Name, client.t("is using a secure connection"))
}
return true
}
if client.HasMode(modes.Operator) { if client.HasMode(modes.Operator) {
masks := strings.Split(masksString, ",") for _, mask := range strings.Split(masksString, ",") {
for _, mask := range masks { matches := server.clients.FindAll(mask)
casefoldedMask, err := Casefold(mask) if len(matches) == 0 && !handleService(mask) {
if err != nil {
rb.Add(nil, client.server.name, ERR_NOSUCHNICK, client.nick, mask, client.t("No such nick"))
continue
}
matches := server.clients.FindAll(casefoldedMask)
if len(matches) == 0 {
rb.Add(nil, client.server.name, ERR_NOSUCHNICK, client.nick, mask, client.t("No such nick")) rb.Add(nil, client.server.name, ERR_NOSUCHNICK, client.nick, mask, client.t("No such nick"))
continue continue
} }
@ -2496,15 +2505,15 @@ func whoisHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Res
} }
} }
} else { } else {
// only get the first request // only get the first request; also require a nick, not a mask
casefoldedMask, err := Casefold(strings.Split(masksString, ",")[0]) nick := strings.Split(masksString, ",")[0]
mclient := server.clients.Get(casefoldedMask) mclient := server.clients.Get(nick)
if err != nil || mclient == nil { if mclient != nil {
rb.Add(nil, client.server.name, ERR_NOSUCHNICK, client.nick, masksString, client.t("No such nick"))
// fall through, ENDOFWHOIS is always sent
} else {
client.getWhoisOf(mclient, rb) client.getWhoisOf(mclient, rb)
} else if !handleService(nick) {
rb.Add(nil, client.server.name, ERR_NOSUCHNICK, client.nick, masksString, client.t("No such nick"))
} }
// fall through, ENDOFWHOIS is always sent
} }
rb.Add(nil, server.name, RPL_ENDOFWHOIS, client.nick, masksString, client.t("End of /WHOIS list")) rb.Add(nil, server.name, RPL_ENDOFWHOIS, client.nick, masksString, client.t("End of /WHOIS list"))
return false return false

View File

@ -3,8 +3,11 @@
package isupport package isupport
import "fmt" import (
import "sort" "fmt"
"sort"
"strings"
)
const ( const (
maxLastArgLength = 400 maxLastArgLength = 400
@ -102,7 +105,7 @@ func (il *List) GetDifference(newil *List) [][]string {
} }
// RegenerateCachedReply regenerates the cached RPL_ISUPPORT reply // RegenerateCachedReply regenerates the cached RPL_ISUPPORT reply
func (il *List) RegenerateCachedReply() { func (il *List) RegenerateCachedReply() (err error) {
il.CachedReply = make([][]string, 0) il.CachedReply = make([][]string, 0)
var length int // Length of the current cache var length int // Length of the current cache
var cache []string // Token list cache var cache []string // Token list cache
@ -116,6 +119,10 @@ func (il *List) RegenerateCachedReply() {
for _, name := range tokens { for _, name := range tokens {
token := getTokenString(name, il.Tokens[name]) token := getTokenString(name, il.Tokens[name])
if token[0] == ':' || strings.Contains(token, " ") {
err = fmt.Errorf("bad isupport token (cannot contain spaces or start with :): %s", token)
continue
}
if len(token)+length <= maxLastArgLength { if len(token)+length <= maxLastArgLength {
// account for the space separating tokens // account for the space separating tokens
@ -136,4 +143,6 @@ func (il *List) RegenerateCachedReply() {
if len(cache) > 0 { if len(cache) > 0 {
il.CachedReply = append(il.CachedReply, cache) il.CachedReply = append(il.CachedReply, cache)
} }
return
} }

View File

@ -26,7 +26,10 @@ func TestISUPPORT(t *testing.T) {
tListLong.AddNoValue("D") tListLong.AddNoValue("D")
tListLong.AddNoValue("E") tListLong.AddNoValue("E")
tListLong.AddNoValue("F") tListLong.AddNoValue("F")
tListLong.RegenerateCachedReply() err := tListLong.RegenerateCachedReply()
if err != nil {
t.Error(err)
}
longReplies := [][]string{ longReplies := [][]string{
{"1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D"}, {"1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D"},
@ -44,7 +47,10 @@ func TestISUPPORT(t *testing.T) {
tList1.Add("INVEX", "i") tList1.Add("INVEX", "i")
tList1.AddNoValue("EXTBAN") tList1.AddNoValue("EXTBAN")
tList1.Add("RANDKILL", "whenever") tList1.Add("RANDKILL", "whenever")
tList1.RegenerateCachedReply() err = tList1.RegenerateCachedReply()
if err != nil {
t.Error(err)
}
expected := [][]string{{"CASEMAPPING=rfc1459-strict", "EXTBAN", "INVEX=i", "RANDKILL=whenever", "SASL=yes"}} expected := [][]string{{"CASEMAPPING=rfc1459-strict", "EXTBAN", "INVEX=i", "RANDKILL=whenever", "SASL=yes"}}
if !reflect.DeepEqual(tList1.CachedReply, expected) { if !reflect.DeepEqual(tList1.CachedReply, expected) {
@ -58,7 +64,10 @@ func TestISUPPORT(t *testing.T) {
tList2.AddNoValue("INVEX") tList2.AddNoValue("INVEX")
tList2.Add("EXTBAN", "TestBah") tList2.Add("EXTBAN", "TestBah")
tList2.AddNoValue("STABLEKILL") tList2.AddNoValue("STABLEKILL")
tList2.RegenerateCachedReply() err = tList2.RegenerateCachedReply()
if err != nil {
t.Error(err)
}
expected = [][]string{{"CASEMAPPING=ascii", "EXTBAN=TestBah", "INVEX", "SASL=yes", "STABLEKILL"}} expected = [][]string{{"CASEMAPPING=ascii", "EXTBAN=TestBah", "INVEX", "SASL=yes", "STABLEKILL"}}
if !reflect.DeepEqual(tList2.CachedReply, expected) { if !reflect.DeepEqual(tList2.CachedReply, expected) {
@ -72,3 +81,26 @@ func TestISUPPORT(t *testing.T) {
t.Error("difference reply does not match expected difference reply") t.Error("difference reply does not match expected difference reply")
} }
} }
func TestBadToken(t *testing.T) {
list := NewList()
list.Add("NETWORK", "Bad Network Name")
list.Add("SASL", "yes")
list.Add("CASEMAPPING", "rfc1459-strict")
list.Add("INVEX", "i")
list.AddNoValue("EXTBAN")
err := list.RegenerateCachedReply()
if err == nil {
t.Error("isupport token generation should fail due to space in network name")
}
// should produce a list containing the other, valid params
numParams := 0
for _, tokenLine := range list.CachedReply {
numParams += len(tokenLine)
}
if numParams != 4 {
t.Errorf("expected the other 4 params to be generated, got %v", list.CachedReply)
}
}

View File

@ -250,7 +250,7 @@ func (logger *singleLogger) Log(level Level, logType string, messageParts ...str
} }
sep := grey(":") sep := grey(":")
fullStringFormatted := fmt.Sprintf("%s %s %s %s %s %s ", timeGrey(time.Now().UTC().Format("2006-01-02T15:04:05Z")), sep, levelDisplay, sep, section(logType), sep) fullStringFormatted := fmt.Sprintf("%s %s %s %s %s %s ", timeGrey(time.Now().UTC().Format("2006-01-02T15:04:05.000Z")), sep, levelDisplay, sep, section(logType), sep)
fullStringRaw := fmt.Sprintf("%s : %s : %s : ", time.Now().UTC().Format("2006-01-02T15:04:05Z"), LogLevelDisplayNames[level], logType) fullStringRaw := fmt.Sprintf("%s : %s : %s : ", time.Now().UTC().Format("2006-01-02T15:04:05Z"), LogLevelDisplayNames[level], logType)
for i, p := range messageParts { for i, p := range messageParts {
fullStringFormatted += p fullStringFormatted += p

View File

@ -9,7 +9,6 @@ import (
"bufio" "bufio"
"crypto/tls" "crypto/tls"
"fmt" "fmt"
"math/rand"
"net" "net"
"net/http" "net/http"
_ "net/http/pprof" _ "net/http/pprof"
@ -148,7 +147,7 @@ func NewServer(config *Config, logger *logger.Manager) (*Server, error) {
} }
// setISupport sets up our RPL_ISUPPORT reply. // setISupport sets up our RPL_ISUPPORT reply.
func (server *Server) setISupport() { func (server *Server) setISupport() (err error) {
maxTargetsString := strconv.Itoa(maxTargets) maxTargetsString := strconv.Itoa(maxTargets)
config := server.Config() config := server.Config()
@ -193,11 +192,15 @@ func (server *Server) setISupport() {
isupport.Add("REGCREDTYPES", "passphrase,certfp") isupport.Add("REGCREDTYPES", "passphrase,certfp")
} }
isupport.RegenerateCachedReply() err = isupport.RegenerateCachedReply()
if err != nil {
return
}
server.configurableStateMutex.Lock() server.configurableStateMutex.Lock()
server.isupport = isupport server.isupport = isupport
server.configurableStateMutex.Unlock() server.configurableStateMutex.Unlock()
return
} }
func loadChannelList(channel *Channel, list string, maskMode modes.Mode) { func loadChannelList(channel *Channel, list string, maskMode modes.Mode) {
@ -371,13 +374,7 @@ func (server *Server) createListener(addr string, tlsConfig *tls.Config, bindMod
// generateMessageID returns a network-unique message ID. // generateMessageID returns a network-unique message ID.
func (server *Server) generateMessageID() string { func (server *Server) generateMessageID() string {
// we don't need the full like 30 chars since the unixnano below handles return utils.GenerateSecretToken()
// most of our uniqueness requirements, so just truncate at 5
lastbit := strconv.FormatInt(rand.Int63(), 36)
if 5 < len(lastbit) {
lastbit = lastbit[:4]
}
return fmt.Sprintf("%s%s", strconv.FormatInt(time.Now().UTC().UnixNano(), 36), lastbit)
} }
// //
@ -794,7 +791,10 @@ func (server *Server) applyConfig(config *Config, initial bool) (err error) {
// set RPL_ISUPPORT // set RPL_ISUPPORT
var newISupportReplies [][]string var newISupportReplies [][]string
oldISupportList := server.ISupport() oldISupportList := server.ISupport()
server.setISupport() err = server.setISupport()
if err != nil {
return err
}
if oldISupportList != nil { if oldISupportList != nil {
newISupportReplies = oldISupportList.GetDifference(server.ISupport()) newISupportReplies = oldISupportList.GetDifference(server.ISupport())
} }

View File

@ -8,10 +8,8 @@ package main
import ( import (
"fmt" "fmt"
"log" "log"
"math/rand"
"strings" "strings"
"syscall" "syscall"
"time"
"github.com/docopt/docopt-go" "github.com/docopt/docopt-go"
"github.com/oragono/oragono/irc" "github.com/oragono/oragono/irc"
@ -114,7 +112,6 @@ Options:
} }
} }
} else if arguments["run"].(bool) { } else if arguments["run"].(bool) {
rand.Seed(time.Now().UTC().UnixNano())
if !arguments["--quiet"].(bool) { if !arguments["--quiet"].(bool) {
logman.Info("startup", fmt.Sprintf("Oragono v%s starting", irc.SemVer)) logman.Info("startup", fmt.Sprintf("Oragono v%s starting", irc.SemVer))
if commit == "" { if commit == "" {