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

View File

@ -28,7 +28,7 @@ import (
const (
// IdentTimeoutSeconds is how many seconds before our ident (username) check times out.
IdentTimeoutSeconds = 1.5
IRCv3TimestampFormat = "2006-01-02T15:04:05.999Z"
IRCv3TimestampFormat = "2006-01-02T15:04:05.000Z"
)
var (
@ -332,12 +332,6 @@ func (client *Client) Active() {
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.
func (client *Client) Ping() {
client.Send(nil, "", "PING", client.nick)

View File

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

View File

@ -12,13 +12,12 @@ import (
// Command represents a command accepted from a client.
type Command struct {
handler func(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool
oper 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
minParams int
capabs []string
handler func(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool
oper bool
usablePreReg bool
leaveClientIdle bool // if true, leaves the client active time alone
minParams int
capabs []string
}
// 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)
}
if !cmd.leaveClientIdle {
client.Touch()
}
// most servers do this only for PING/PONG, but we'll do it for any command:
client.idletimer.Touch()
if !cmd.leaveClientActive {
if !cmd.leaveClientIdle {
client.Active()
}
@ -118,8 +116,9 @@ func init() {
minParams: 2,
},
"ISON": {
handler: isonHandler,
minParams: 1,
handler: isonHandler,
minParams: 1,
leaveClientIdle: true,
},
"JOIN": {
handler: joinHandler,
@ -200,16 +199,16 @@ func init() {
minParams: 1,
},
"PING": {
handler: pingHandler,
usablePreReg: true,
minParams: 1,
leaveClientActive: true,
handler: pingHandler,
usablePreReg: true,
minParams: 1,
leaveClientIdle: true,
},
"PONG": {
handler: pongHandler,
usablePreReg: true,
minParams: 1,
leaveClientActive: true,
handler: pongHandler,
usablePreReg: true,
minParams: 1,
leaveClientIdle: true,
},
"PRIVMSG": {
handler: privmsgHandler,

View File

@ -2478,16 +2478,25 @@ func whoisHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Res
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) {
masks := strings.Split(masksString, ",")
for _, mask := range masks {
casefoldedMask, err := Casefold(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 {
for _, mask := range strings.Split(masksString, ",") {
matches := server.clients.FindAll(mask)
if len(matches) == 0 && !handleService(mask) {
rb.Add(nil, client.server.name, ERR_NOSUCHNICK, client.nick, mask, client.t("No such nick"))
continue
}
@ -2496,15 +2505,15 @@ func whoisHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Res
}
}
} else {
// only get the first request
casefoldedMask, err := Casefold(strings.Split(masksString, ",")[0])
mclient := server.clients.Get(casefoldedMask)
if err != nil || 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 {
// only get the first request; also require a nick, not a mask
nick := strings.Split(masksString, ",")[0]
mclient := server.clients.Get(nick)
if mclient != nil {
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"))
return false

View File

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

View File

@ -26,7 +26,10 @@ func TestISUPPORT(t *testing.T) {
tListLong.AddNoValue("D")
tListLong.AddNoValue("E")
tListLong.AddNoValue("F")
tListLong.RegenerateCachedReply()
err := tListLong.RegenerateCachedReply()
if err != nil {
t.Error(err)
}
longReplies := [][]string{
{"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.AddNoValue("EXTBAN")
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"}}
if !reflect.DeepEqual(tList1.CachedReply, expected) {
@ -58,7 +64,10 @@ func TestISUPPORT(t *testing.T) {
tList2.AddNoValue("INVEX")
tList2.Add("EXTBAN", "TestBah")
tList2.AddNoValue("STABLEKILL")
tList2.RegenerateCachedReply()
err = tList2.RegenerateCachedReply()
if err != nil {
t.Error(err)
}
expected = [][]string{{"CASEMAPPING=ascii", "EXTBAN=TestBah", "INVEX", "SASL=yes", "STABLEKILL"}}
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")
}
}
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(":")
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)
for i, p := range messageParts {
fullStringFormatted += p

View File

@ -9,7 +9,6 @@ import (
"bufio"
"crypto/tls"
"fmt"
"math/rand"
"net"
"net/http"
_ "net/http/pprof"
@ -148,7 +147,7 @@ func NewServer(config *Config, logger *logger.Manager) (*Server, error) {
}
// setISupport sets up our RPL_ISUPPORT reply.
func (server *Server) setISupport() {
func (server *Server) setISupport() (err error) {
maxTargetsString := strconv.Itoa(maxTargets)
config := server.Config()
@ -193,11 +192,15 @@ func (server *Server) setISupport() {
isupport.Add("REGCREDTYPES", "passphrase,certfp")
}
isupport.RegenerateCachedReply()
err = isupport.RegenerateCachedReply()
if err != nil {
return
}
server.configurableStateMutex.Lock()
server.isupport = isupport
server.configurableStateMutex.Unlock()
return
}
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.
func (server *Server) generateMessageID() string {
// we don't need the full like 30 chars since the unixnano below handles
// 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)
return utils.GenerateSecretToken()
}
//
@ -794,7 +791,10 @@ func (server *Server) applyConfig(config *Config, initial bool) (err error) {
// set RPL_ISUPPORT
var newISupportReplies [][]string
oldISupportList := server.ISupport()
server.setISupport()
err = server.setISupport()
if err != nil {
return err
}
if oldISupportList != nil {
newISupportReplies = oldISupportList.GetDifference(server.ISupport())
}

View File

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