mirror of
https://github.com/ergochat/ergo.git
synced 2024-11-10 22:19:31 +01:00
commit
847922e53d
@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -15,8 +15,7 @@ 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
|
||||
leaveClientIdle bool // if true, leaves the client active time alone
|
||||
minParams int
|
||||
capabs []string
|
||||
}
|
||||
@ -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()
|
||||
}
|
||||
|
||||
@ -120,6 +118,7 @@ func init() {
|
||||
"ISON": {
|
||||
handler: isonHandler,
|
||||
minParams: 1,
|
||||
leaveClientIdle: true,
|
||||
},
|
||||
"JOIN": {
|
||||
handler: joinHandler,
|
||||
@ -203,13 +202,13 @@ func init() {
|
||||
handler: pingHandler,
|
||||
usablePreReg: true,
|
||||
minParams: 1,
|
||||
leaveClientActive: true,
|
||||
leaveClientIdle: true,
|
||||
},
|
||||
"PONG": {
|
||||
handler: pongHandler,
|
||||
usablePreReg: true,
|
||||
minParams: 1,
|
||||
leaveClientActive: true,
|
||||
leaveClientIdle: true,
|
||||
},
|
||||
"PRIVMSG": {
|
||||
handler: privmsgHandler,
|
||||
|
@ -2478,16 +2478,25 @@ func whoisHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Res
|
||||
return false
|
||||
}
|
||||
|
||||
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
|
||||
handleService := func(nick string) bool {
|
||||
cfnick, _ := CasefoldName(nick)
|
||||
service, ok := OragonoServices[cfnick]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
matches := server.clients.FindAll(casefoldedMask)
|
||||
if len(matches) == 0 {
|
||||
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) {
|
||||
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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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())
|
||||
}
|
||||
|
@ -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 == "" {
|
||||
|
Loading…
Reference in New Issue
Block a user