Ver código fonte

Merge pull request #321 from slingamn/misc.2

eight small changes
tags/v1.0.0-rc1
Daniel Oaks 5 anos atrás
pai
commit
847922e53d
Nenhuma conta vinculada ao e-mail do autor do commit
10 arquivos alterados com 120 adições e 74 exclusões
  1. 13
    7
      irc/channel.go
  2. 1
    7
      irc/client.go
  3. 2
    2
      irc/client_lookup_set.go
  4. 20
    21
      irc/commands.go
  5. 25
    16
      irc/handlers.go
  6. 12
    3
      irc/isupport/list.go
  7. 35
    3
      irc/isupport/list_test.go
  8. 1
    1
      irc/logger/logger.go
  9. 11
    11
      irc/server.go
  10. 0
    3
      oragono.go

+ 13
- 7
irc/channel.go Ver arquivo

@@ -346,6 +346,9 @@ func (channel *Channel) IsEmpty() bool {
346 346
 
347 347
 // Join joins the given client to this channel (if they can be joined).
348 348
 func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *ResponseBuffer) {
349
+	account := client.Account()
350
+	nickMaskCasefolded := client.NickMaskCasefolded()
351
+
349 352
 	channel.stateMutex.RLock()
350 353
 	chname := channel.name
351 354
 	chcfname := channel.nameCasefolded
@@ -354,6 +357,7 @@ func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *Resp
354 357
 	limit := channel.userLimit
355 358
 	chcount := len(channel.members)
356 359
 	_, alreadyJoined := channel.members[client]
360
+	persistentMode := channel.accountToUMode[account]
357 361
 	channel.stateMutex.RUnlock()
358 362
 
359 363
 	if alreadyJoined {
@@ -361,9 +365,9 @@ func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *Resp
361 365
 		return
362 366
 	}
363 367
 
364
-	account := client.Account()
365
-	nickMaskCasefolded := client.NickMaskCasefolded()
366
-	hasPrivs := isSajoin || (founder != "" && founder == account)
368
+	// the founder can always join (even if they disabled auto +q on join);
369
+	// anyone who automatically receives halfop or higher can always join
370
+	hasPrivs := isSajoin || (founder != "" && founder == account) || (persistentMode != 0 && persistentMode != modes.Voice)
367 371
 
368 372
 	if !hasPrivs && limit != 0 && chcount >= limit {
369 373
 		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
404 408
 			if newChannel {
405 409
 				givenMode = modes.ChannelOperator
406 410
 			} else {
407
-				givenMode = channel.accountToUMode[account]
411
+				givenMode = persistentMode
408 412
 			}
409 413
 			if givenMode != 0 {
410 414
 				channel.members[client].SetMode(givenMode, true)
@@ -803,6 +807,8 @@ func (channel *Channel) sendSplitMessage(msgid, cmd string, histType history.Ite
803 807
 	nickmask := client.NickMaskString()
804 808
 	account := client.AccountName()
805 809
 
810
+	now := time.Now().UTC()
811
+
806 812
 	for _, member := range channel.Members() {
807 813
 		if minPrefix != nil && !channel.ClientIsAtLeast(member, minPrefixMode) {
808 814
 			// STATUSMSG
@@ -817,11 +823,10 @@ func (channel *Channel) sendSplitMessage(msgid, cmd string, histType history.Ite
817 823
 			tagsToUse = clientOnlyTags
818 824
 		}
819 825
 
820
-		// TODO(slingamn) evaluate an optimization where we reuse `nickmask` and `account`
821 826
 		if message == nil {
822
-			member.SendFromClient(msgid, client, tagsToUse, cmd, channel.name)
827
+			member.sendFromClientInternal(false, now, msgid, nickmask, account, tagsToUse, cmd, channel.name)
823 828
 		} else {
824
-			member.SendSplitMsgFromClient(msgid, client, tagsToUse, cmd, channel.name, *message)
829
+			member.sendSplitMsgFromClientInternal(false, now, msgid, nickmask, account, tagsToUse, cmd, channel.name, *message)
825 830
 		}
826 831
 	}
827 832
 
@@ -831,6 +836,7 @@ func (channel *Channel) sendSplitMessage(msgid, cmd string, histType history.Ite
831 836
 		Message:     *message,
832 837
 		Nick:        nickmask,
833 838
 		AccountName: account,
839
+		Time:        now,
834 840
 	})
835 841
 }
836 842
 

+ 1
- 7
irc/client.go Ver arquivo

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

+ 2
- 2
irc/client_lookup_set.go Ver arquivo

@@ -189,7 +189,7 @@ func (clients *ClientManager) FindAll(userhost string) (set ClientSet) {
189 189
 	clients.RLock()
190 190
 	defer clients.RUnlock()
191 191
 	for _, client := range clients.byNick {
192
-		if matcher.Match(client.nickMaskCasefolded) {
192
+		if matcher.Match(client.NickMaskCasefolded()) {
193 193
 			set.Add(client)
194 194
 		}
195 195
 	}
@@ -209,7 +209,7 @@ func (clients *ClientManager) Find(userhost string) *Client {
209 209
 	clients.RLock()
210 210
 	defer clients.RUnlock()
211 211
 	for _, client := range clients.byNick {
212
-		if matcher.Match(client.nickMaskCasefolded) {
212
+		if matcher.Match(client.NickMaskCasefolded()) {
213 213
 			matchedClient = client
214 214
 			break
215 215
 		}

+ 20
- 21
irc/commands.go Ver arquivo

@@ -12,13 +12,12 @@ import (
12 12
 
13 13
 // Command represents a command accepted from a client.
14 14
 type Command struct {
15
-	handler           func(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool
16
-	oper              bool
17
-	usablePreReg      bool
18
-	leaveClientActive bool // if true, leaves the client active time alone. reversed because we can't default a struct element to True
19
-	leaveClientIdle   bool
20
-	minParams         int
21
-	capabs            []string
15
+	handler         func(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool
16
+	oper            bool
17
+	usablePreReg    bool
18
+	leaveClientIdle bool // if true, leaves the client active time alone
19
+	minParams       int
20
+	capabs          []string
22 21
 }
23 22
 
24 23
 // 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
54 53
 		server.tryRegister(client)
55 54
 	}
56 55
 
57
-	if !cmd.leaveClientIdle {
58
-		client.Touch()
59
-	}
56
+	// most servers do this only for PING/PONG, but we'll do it for any command:
57
+	client.idletimer.Touch()
60 58
 
61
-	if !cmd.leaveClientActive {
59
+	if !cmd.leaveClientIdle {
62 60
 		client.Active()
63 61
 	}
64 62
 
@@ -118,8 +116,9 @@ func init() {
118 116
 			minParams: 2,
119 117
 		},
120 118
 		"ISON": {
121
-			handler:   isonHandler,
122
-			minParams: 1,
119
+			handler:         isonHandler,
120
+			minParams:       1,
121
+			leaveClientIdle: true,
123 122
 		},
124 123
 		"JOIN": {
125 124
 			handler:   joinHandler,
@@ -200,16 +199,16 @@ func init() {
200 199
 			minParams:    1,
201 200
 		},
202 201
 		"PING": {
203
-			handler:           pingHandler,
204
-			usablePreReg:      true,
205
-			minParams:         1,
206
-			leaveClientActive: true,
202
+			handler:         pingHandler,
203
+			usablePreReg:    true,
204
+			minParams:       1,
205
+			leaveClientIdle: true,
207 206
 		},
208 207
 		"PONG": {
209
-			handler:           pongHandler,
210
-			usablePreReg:      true,
211
-			minParams:         1,
212
-			leaveClientActive: true,
208
+			handler:         pongHandler,
209
+			usablePreReg:    true,
210
+			minParams:       1,
211
+			leaveClientIdle: true,
213 212
 		},
214 213
 		"PRIVMSG": {
215 214
 			handler:   privmsgHandler,

+ 25
- 16
irc/handlers.go Ver arquivo

@@ -2478,16 +2478,25 @@ func whoisHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Res
2478 2478
 		return false
2479 2479
 	}
2480 2480
 
2481
+	handleService := func(nick string) bool {
2482
+		cfnick, _ := CasefoldName(nick)
2483
+		service, ok := OragonoServices[cfnick]
2484
+		if !ok {
2485
+			return false
2486
+		}
2487
+		clientNick := client.Nick()
2488
+		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))
2489
+		// hehe
2490
+		if client.HasMode(modes.TLS) {
2491
+			rb.Add(nil, client.server.name, RPL_WHOISSECURE, clientNick, service.Name, client.t("is using a secure connection"))
2492
+		}
2493
+		return true
2494
+	}
2495
+
2481 2496
 	if client.HasMode(modes.Operator) {
2482
-		masks := strings.Split(masksString, ",")
2483
-		for _, mask := range masks {
2484
-			casefoldedMask, err := Casefold(mask)
2485
-			if err != nil {
2486
-				rb.Add(nil, client.server.name, ERR_NOSUCHNICK, client.nick, mask, client.t("No such nick"))
2487
-				continue
2488
-			}
2489
-			matches := server.clients.FindAll(casefoldedMask)
2490
-			if len(matches) == 0 {
2497
+		for _, mask := range strings.Split(masksString, ",") {
2498
+			matches := server.clients.FindAll(mask)
2499
+			if len(matches) == 0 && !handleService(mask) {
2491 2500
 				rb.Add(nil, client.server.name, ERR_NOSUCHNICK, client.nick, mask, client.t("No such nick"))
2492 2501
 				continue
2493 2502
 			}
@@ -2496,15 +2505,15 @@ func whoisHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Res
2496 2505
 			}
2497 2506
 		}
2498 2507
 	} else {
2499
-		// only get the first request
2500
-		casefoldedMask, err := Casefold(strings.Split(masksString, ",")[0])
2501
-		mclient := server.clients.Get(casefoldedMask)
2502
-		if err != nil || mclient == nil {
2503
-			rb.Add(nil, client.server.name, ERR_NOSUCHNICK, client.nick, masksString, client.t("No such nick"))
2504
-			// fall through, ENDOFWHOIS is always sent
2505
-		} else {
2508
+		// only get the first request; also require a nick, not a mask
2509
+		nick := strings.Split(masksString, ",")[0]
2510
+		mclient := server.clients.Get(nick)
2511
+		if mclient != nil {
2506 2512
 			client.getWhoisOf(mclient, rb)
2513
+		} else if !handleService(nick) {
2514
+			rb.Add(nil, client.server.name, ERR_NOSUCHNICK, client.nick, masksString, client.t("No such nick"))
2507 2515
 		}
2516
+		// fall through, ENDOFWHOIS is always sent
2508 2517
 	}
2509 2518
 	rb.Add(nil, server.name, RPL_ENDOFWHOIS, client.nick, masksString, client.t("End of /WHOIS list"))
2510 2519
 	return false

+ 12
- 3
irc/isupport/list.go Ver arquivo

@@ -3,8 +3,11 @@
3 3
 
4 4
 package isupport
5 5
 
6
-import "fmt"
7
-import "sort"
6
+import (
7
+	"fmt"
8
+	"sort"
9
+	"strings"
10
+)
8 11
 
9 12
 const (
10 13
 	maxLastArgLength = 400
@@ -102,7 +105,7 @@ func (il *List) GetDifference(newil *List) [][]string {
102 105
 }
103 106
 
104 107
 // RegenerateCachedReply regenerates the cached RPL_ISUPPORT reply
105
-func (il *List) RegenerateCachedReply() {
108
+func (il *List) RegenerateCachedReply() (err error) {
106 109
 	il.CachedReply = make([][]string, 0)
107 110
 	var length int     // Length of the current cache
108 111
 	var cache []string // Token list cache
@@ -116,6 +119,10 @@ func (il *List) RegenerateCachedReply() {
116 119
 
117 120
 	for _, name := range tokens {
118 121
 		token := getTokenString(name, il.Tokens[name])
122
+		if token[0] == ':' || strings.Contains(token, " ") {
123
+			err = fmt.Errorf("bad isupport token (cannot contain spaces or start with :): %s", token)
124
+			continue
125
+		}
119 126
 
120 127
 		if len(token)+length <= maxLastArgLength {
121 128
 			// account for the space separating tokens
@@ -136,4 +143,6 @@ func (il *List) RegenerateCachedReply() {
136 143
 	if len(cache) > 0 {
137 144
 		il.CachedReply = append(il.CachedReply, cache)
138 145
 	}
146
+
147
+	return
139 148
 }

+ 35
- 3
irc/isupport/list_test.go Ver arquivo

@@ -26,7 +26,10 @@ func TestISUPPORT(t *testing.T) {
26 26
 	tListLong.AddNoValue("D")
27 27
 	tListLong.AddNoValue("E")
28 28
 	tListLong.AddNoValue("F")
29
-	tListLong.RegenerateCachedReply()
29
+	err := tListLong.RegenerateCachedReply()
30
+	if err != nil {
31
+		t.Error(err)
32
+	}
30 33
 
31 34
 	longReplies := [][]string{
32 35
 		{"1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D"},
@@ -44,7 +47,10 @@ func TestISUPPORT(t *testing.T) {
44 47
 	tList1.Add("INVEX", "i")
45 48
 	tList1.AddNoValue("EXTBAN")
46 49
 	tList1.Add("RANDKILL", "whenever")
47
-	tList1.RegenerateCachedReply()
50
+	err = tList1.RegenerateCachedReply()
51
+	if err != nil {
52
+		t.Error(err)
53
+	}
48 54
 
49 55
 	expected := [][]string{{"CASEMAPPING=rfc1459-strict", "EXTBAN", "INVEX=i", "RANDKILL=whenever", "SASL=yes"}}
50 56
 	if !reflect.DeepEqual(tList1.CachedReply, expected) {
@@ -58,7 +64,10 @@ func TestISUPPORT(t *testing.T) {
58 64
 	tList2.AddNoValue("INVEX")
59 65
 	tList2.Add("EXTBAN", "TestBah")
60 66
 	tList2.AddNoValue("STABLEKILL")
61
-	tList2.RegenerateCachedReply()
67
+	err = tList2.RegenerateCachedReply()
68
+	if err != nil {
69
+		t.Error(err)
70
+	}
62 71
 
63 72
 	expected = [][]string{{"CASEMAPPING=ascii", "EXTBAN=TestBah", "INVEX", "SASL=yes", "STABLEKILL"}}
64 73
 	if !reflect.DeepEqual(tList2.CachedReply, expected) {
@@ -72,3 +81,26 @@ func TestISUPPORT(t *testing.T) {
72 81
 		t.Error("difference reply does not match expected difference reply")
73 82
 	}
74 83
 }
84
+
85
+func TestBadToken(t *testing.T) {
86
+	list := NewList()
87
+	list.Add("NETWORK", "Bad Network Name")
88
+	list.Add("SASL", "yes")
89
+	list.Add("CASEMAPPING", "rfc1459-strict")
90
+	list.Add("INVEX", "i")
91
+	list.AddNoValue("EXTBAN")
92
+
93
+	err := list.RegenerateCachedReply()
94
+	if err == nil {
95
+		t.Error("isupport token generation should fail due to space in network name")
96
+	}
97
+
98
+	// should produce a list containing the other, valid params
99
+	numParams := 0
100
+	for _, tokenLine := range list.CachedReply {
101
+		numParams += len(tokenLine)
102
+	}
103
+	if numParams != 4 {
104
+		t.Errorf("expected the other 4 params to be generated, got %v", list.CachedReply)
105
+	}
106
+}

+ 1
- 1
irc/logger/logger.go Ver arquivo

@@ -250,7 +250,7 @@ func (logger *singleLogger) Log(level Level, logType string, messageParts ...str
250 250
 	}
251 251
 
252 252
 	sep := grey(":")
253
-	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)
253
+	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)
254 254
 	fullStringRaw := fmt.Sprintf("%s : %s : %s : ", time.Now().UTC().Format("2006-01-02T15:04:05Z"), LogLevelDisplayNames[level], logType)
255 255
 	for i, p := range messageParts {
256 256
 		fullStringFormatted += p

+ 11
- 11
irc/server.go Ver arquivo

@@ -9,7 +9,6 @@ import (
9 9
 	"bufio"
10 10
 	"crypto/tls"
11 11
 	"fmt"
12
-	"math/rand"
13 12
 	"net"
14 13
 	"net/http"
15 14
 	_ "net/http/pprof"
@@ -148,7 +147,7 @@ func NewServer(config *Config, logger *logger.Manager) (*Server, error) {
148 147
 }
149 148
 
150 149
 // setISupport sets up our RPL_ISUPPORT reply.
151
-func (server *Server) setISupport() {
150
+func (server *Server) setISupport() (err error) {
152 151
 	maxTargetsString := strconv.Itoa(maxTargets)
153 152
 
154 153
 	config := server.Config()
@@ -193,11 +192,15 @@ func (server *Server) setISupport() {
193 192
 		isupport.Add("REGCREDTYPES", "passphrase,certfp")
194 193
 	}
195 194
 
196
-	isupport.RegenerateCachedReply()
195
+	err = isupport.RegenerateCachedReply()
196
+	if err != nil {
197
+		return
198
+	}
197 199
 
198 200
 	server.configurableStateMutex.Lock()
199 201
 	server.isupport = isupport
200 202
 	server.configurableStateMutex.Unlock()
203
+	return
201 204
 }
202 205
 
203 206
 func loadChannelList(channel *Channel, list string, maskMode modes.Mode) {
@@ -371,13 +374,7 @@ func (server *Server) createListener(addr string, tlsConfig *tls.Config, bindMod
371 374
 
372 375
 // generateMessageID returns a network-unique message ID.
373 376
 func (server *Server) generateMessageID() string {
374
-	// we don't need the full like 30 chars since the unixnano below handles
375
-	// most of our uniqueness requirements, so just truncate at 5
376
-	lastbit := strconv.FormatInt(rand.Int63(), 36)
377
-	if 5 < len(lastbit) {
378
-		lastbit = lastbit[:4]
379
-	}
380
-	return fmt.Sprintf("%s%s", strconv.FormatInt(time.Now().UTC().UnixNano(), 36), lastbit)
377
+	return utils.GenerateSecretToken()
381 378
 }
382 379
 
383 380
 //
@@ -794,7 +791,10 @@ func (server *Server) applyConfig(config *Config, initial bool) (err error) {
794 791
 	// set RPL_ISUPPORT
795 792
 	var newISupportReplies [][]string
796 793
 	oldISupportList := server.ISupport()
797
-	server.setISupport()
794
+	err = server.setISupport()
795
+	if err != nil {
796
+		return err
797
+	}
798 798
 	if oldISupportList != nil {
799 799
 		newISupportReplies = oldISupportList.GetDifference(server.ISupport())
800 800
 	}

+ 0
- 3
oragono.go Ver arquivo

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

Carregando…
Cancelar
Salvar