소스 검색

modes refactor, #255

tags/v0.12.0
Shivaram Lingamneni 6 년 전
부모
커밋
fad2475c3f
14개의 변경된 파일307개의 추가작업 그리고 285개의 파일을 삭제
  1. 4
    2
      Makefile
  2. 34
    48
      irc/channel.go
  3. 2
    2
      irc/chanserv.go
  4. 4
    10
      irc/client.go
  5. 8
    2
      irc/config.go
  6. 1
    5
      irc/gateways.go
  7. 6
    24
      irc/getters.go
  8. 28
    45
      irc/handlers.go
  9. 33
    111
      irc/modes.go
  10. 136
    13
      irc/modes/modes.go
  11. 37
    0
      irc/modes/modes_test.go
  12. 3
    3
      irc/roleplay.go
  13. 8
    8
      irc/server.go
  14. 3
    12
      irc/types.go

+ 4
- 2
Makefile 파일 보기

@@ -12,5 +12,7 @@ deps:
12 12
 	git submodule update --init
13 13
 
14 14
 test:
15
-	cd irc && go test .
16
-	cd irc && go vet .
15
+	cd irc && go test . && go vet .
16
+	cd irc/isupport && go test . && go vet .
17
+	cd irc/modes && go test . && go vet .
18
+	cd irc/utils && go test . && go vet .

+ 34
- 48
irc/channel.go 파일 보기

@@ -20,7 +20,7 @@ import (
20 20
 
21 21
 // Channel represents a channel that clients can join.
22 22
 type Channel struct {
23
-	flags             modes.ModeSet
23
+	flags             *modes.ModeSet
24 24
 	lists             map[modes.Mode]*UserMaskSet
25 25
 	key               string
26 26
 	members           MemberSet
@@ -51,7 +51,7 @@ func NewChannel(s *Server, name string, regInfo *RegisteredChannel) *Channel {
51 51
 
52 52
 	channel := &Channel{
53 53
 		createdTime: time.Now(), // may be overwritten by applyRegInfo
54
-		flags:       make(modes.ModeSet),
54
+		flags:       modes.NewModeSet(),
55 55
 		lists: map[modes.Mode]*UserMaskSet{
56 56
 			modes.BanMask:    NewUserMaskSet(),
57 57
 			modes.ExceptMask: NewUserMaskSet(),
@@ -68,7 +68,7 @@ func NewChannel(s *Server, name string, regInfo *RegisteredChannel) *Channel {
68 68
 		channel.applyRegInfo(regInfo)
69 69
 	} else {
70 70
 		for _, mode := range s.DefaultChannelModes() {
71
-			channel.flags[mode] = true
71
+			channel.flags.SetMode(mode, true)
72 72
 		}
73 73
 	}
74 74
 
@@ -87,7 +87,7 @@ func (channel *Channel) applyRegInfo(chanReg *RegisteredChannel) {
87 87
 	channel.key = chanReg.Key
88 88
 
89 89
 	for _, mode := range chanReg.Modes {
90
-		channel.flags[mode] = true
90
+		channel.flags.SetMode(mode, true)
91 91
 	}
92 92
 	for _, mask := range chanReg.Banlist {
93 93
 		channel.lists[modes.BanMask].Add(mask)
@@ -120,9 +120,7 @@ func (channel *Channel) ExportRegistration(includeFlags uint) (info RegisteredCh
120 120
 
121 121
 	if includeFlags&IncludeModes != 0 {
122 122
 		info.Key = channel.key
123
-		for mode := range channel.flags {
124
-			info.Modes = append(info.Modes, mode)
125
-		}
123
+		info.Modes = channel.flags.AllModes()
126 124
 	}
127 125
 
128 126
 	if includeFlags&IncludeLists != 0 {
@@ -225,14 +223,16 @@ func (channel *Channel) ClientIsAtLeast(client *Client, permission modes.Mode) b
225 223
 	channel.stateMutex.RLock()
226 224
 	defer channel.stateMutex.RUnlock()
227 225
 
226
+	clientModes := channel.members[client]
227
+
228 228
 	// get voice, since it's not a part of ChannelPrivModes
229
-	if channel.members.HasMode(client, permission) {
229
+	if clientModes.HasMode(permission) {
230 230
 		return true
231 231
 	}
232 232
 
233 233
 	// check regular modes
234 234
 	for _, mode := range modes.ChannelPrivModes {
235
-		if channel.members.HasMode(client, mode) {
235
+		if clientModes.HasMode(mode) {
236 236
 			return true
237 237
 		}
238 238
 
@@ -263,14 +263,14 @@ func (channel *Channel) ClientHasPrivsOver(client *Client, target *Client) bool
263 263
 	targetModes := channel.members[target]
264 264
 	result := false
265 265
 	for _, mode := range modes.ChannelPrivModes {
266
-		if clientModes[mode] {
266
+		if clientModes.HasMode(mode) {
267 267
 			result = true
268 268
 			// admins cannot kick other admins
269
-			if mode == modes.ChannelAdmin && targetModes[modes.ChannelAdmin] {
269
+			if mode == modes.ChannelAdmin && targetModes.HasMode(modes.ChannelAdmin) {
270 270
 				result = false
271 271
 			}
272 272
 			break
273
-		} else if channel.members[target][mode] {
273
+		} else if targetModes.HasMode(mode) {
274 274
 			break
275 275
 		}
276 276
 	}
@@ -331,14 +331,11 @@ func (channel *Channel) modeStrings(client *Client) (result []string) {
331 331
 		mods += modes.UserLimit.String()
332 332
 	}
333 333
 
334
+	mods += channel.flags.String()
335
+
334 336
 	channel.stateMutex.RLock()
335 337
 	defer channel.stateMutex.RUnlock()
336 338
 
337
-	// flags
338
-	for mode := range channel.flags {
339
-		mods += mode.String()
340
-	}
341
-
342 339
 	result = []string{mods}
343 340
 
344 341
 	// args for flags with args: The order must match above to keep
@@ -395,7 +392,7 @@ func (channel *Channel) Join(client *Client, key string, rb *ResponseBuffer) {
395 392
 	}
396 393
 
397 394
 	isInvited := channel.lists[modes.InviteMask].Match(client.nickMaskCasefolded)
398
-	if channel.flags[modes.InviteOnly] && !isInvited {
395
+	if channel.flags.HasMode(modes.InviteOnly) && !isInvited {
399 396
 		rb.Add(nil, client.server.name, ERR_INVITEONLYCHAN, channel.name, fmt.Sprintf(client.t("Cannot join channel (+%s)"), "i"))
400 397
 		return
401 398
 	}
@@ -446,7 +443,7 @@ func (channel *Channel) Join(client *Client, key string, rb *ResponseBuffer) {
446 443
 		givenMode = &modes.ChannelOperator
447 444
 	}
448 445
 	if givenMode != nil {
449
-		channel.members[client][*givenMode] = true
446
+		channel.members[client].SetMode(*givenMode, true)
450 447
 	}
451 448
 	channel.stateMutex.Unlock()
452 449
 
@@ -515,12 +512,12 @@ func (channel *Channel) SendTopic(client *Client, rb *ResponseBuffer) {
515 512
 
516 513
 // SetTopic sets the topic of this channel, if the client is allowed to do so.
517 514
 func (channel *Channel) SetTopic(client *Client, topic string, rb *ResponseBuffer) {
518
-	if !(client.flags[modes.Operator] || channel.hasClient(client)) {
515
+	if !(client.HasMode(modes.Operator) || channel.hasClient(client)) {
519 516
 		rb.Add(nil, client.server.name, ERR_NOTONCHANNEL, channel.name, client.t("You're not on that channel"))
520 517
 		return
521 518
 	}
522 519
 
523
-	if channel.HasMode(modes.OpOnlyTopic) && !channel.ClientIsAtLeast(client, modes.ChannelOperator) {
520
+	if channel.flags.HasMode(modes.OpOnlyTopic) && !channel.ClientIsAtLeast(client, modes.ChannelOperator) {
524 521
 		rb.Add(nil, client.server.name, ERR_CHANOPRIVSNEEDED, channel.name, client.t("You're not a channel operator"))
525 522
 		return
526 523
 	}
@@ -552,13 +549,13 @@ func (channel *Channel) CanSpeak(client *Client) bool {
552 549
 	defer channel.stateMutex.RUnlock()
553 550
 
554 551
 	_, hasClient := channel.members[client]
555
-	if channel.flags[modes.NoOutside] && !hasClient {
552
+	if channel.flags.HasMode(modes.NoOutside) && !hasClient {
556 553
 		return false
557 554
 	}
558
-	if channel.flags[modes.Moderated] && !channel.ClientIsAtLeast(client, modes.Voice) {
555
+	if channel.flags.HasMode(modes.Moderated) && !channel.ClientIsAtLeast(client, modes.Voice) {
559 556
 		return false
560 557
 	}
561
-	if channel.flags[modes.RegisteredOnly] && client.Account() == "" {
558
+	if channel.flags.HasMode(modes.RegisteredOnly) && client.Account() == "" {
562 559
 		return false
563 560
 	}
564 561
 	return true
@@ -682,13 +679,7 @@ func (channel *Channel) sendSplitMessage(msgid, cmd string, minPrefix *modes.Mod
682 679
 	}
683 680
 }
684 681
 
685
-func (channel *Channel) applyModeMemberNoMutex(client *Client, mode modes.Mode, op modes.ModeOp, nick string, rb *ResponseBuffer) *modes.ModeChange {
686
-	if nick == "" {
687
-		//TODO(dan): shouldn't this be handled before it reaches this function?
688
-		rb.Add(nil, client.server.name, ERR_NEEDMOREPARAMS, "MODE", client.t("Not enough parameters"))
689
-		return nil
690
-	}
691
-
682
+func (channel *Channel) applyModeToMember(client *Client, mode modes.Mode, op modes.ModeOp, nick string, rb *ResponseBuffer) (result *modes.ModeChange) {
692 683
 	casefoldedName, err := CasefoldName(nick)
693 684
 	target := channel.server.clients.Get(casefoldedName)
694 685
 	if err != nil || target == nil {
@@ -698,26 +689,21 @@ func (channel *Channel) applyModeMemberNoMutex(client *Client, mode modes.Mode,
698 689
 
699 690
 	channel.stateMutex.Lock()
700 691
 	modeset, exists := channel.members[target]
701
-	var already bool
702 692
 	if exists {
703
-		enable := op == modes.Add
704
-		already = modeset[mode] == enable
705
-		modeset[mode] = enable
693
+		if applied := modeset.SetMode(mode, op == modes.Add); applied {
694
+			result = &modes.ModeChange{
695
+				Op:   op,
696
+				Mode: mode,
697
+				Arg:  nick,
698
+			}
699
+		}
706 700
 	}
707 701
 	channel.stateMutex.Unlock()
708 702
 
709 703
 	if !exists {
710 704
 		rb.Add(nil, client.server.name, ERR_USERNOTINCHANNEL, client.nick, channel.name, client.t("They aren't on that channel"))
711
-		return nil
712
-	} else if already {
713
-		return nil
714
-	} else {
715
-		return &modes.ModeChange{
716
-			Op:   op,
717
-			Mode: mode,
718
-			Arg:  nick,
719
-		}
720 705
 	}
706
+	return
721 707
 }
722 708
 
723 709
 // ShowMaskList shows the given list to the client.
@@ -790,7 +776,7 @@ func (channel *Channel) Quit(client *Client) {
790 776
 }
791 777
 
792 778
 func (channel *Channel) Kick(client *Client, target *Client, comment string, rb *ResponseBuffer) {
793
-	if !(client.flags[modes.Operator] || channel.hasClient(client)) {
779
+	if !(client.HasMode(modes.Operator) || channel.hasClient(client)) {
794 780
 		rb.Add(nil, client.server.name, ERR_NOTONCHANNEL, channel.name, client.t("You're not on that channel"))
795 781
 		return
796 782
 	}
@@ -823,7 +809,7 @@ func (channel *Channel) Kick(client *Client, target *Client, comment string, rb
823 809
 
824 810
 // Invite invites the given client to the channel, if the inviter can do so.
825 811
 func (channel *Channel) Invite(invitee *Client, inviter *Client, rb *ResponseBuffer) {
826
-	if channel.flags[modes.InviteOnly] && !channel.ClientIsAtLeast(inviter, modes.ChannelOperator) {
812
+	if channel.flags.HasMode(modes.InviteOnly) && !channel.ClientIsAtLeast(inviter, modes.ChannelOperator) {
827 813
 		rb.Add(nil, inviter.server.name, ERR_CHANOPRIVSNEEDED, channel.name, inviter.t("You're not a channel operator"))
828 814
 		return
829 815
 	}
@@ -834,7 +820,7 @@ func (channel *Channel) Invite(invitee *Client, inviter *Client, rb *ResponseBuf
834 820
 	}
835 821
 
836 822
 	//TODO(dan): handle this more nicely, keep a list of last X invited channels on invitee rather than explicitly modifying the invite list?
837
-	if channel.flags[modes.InviteOnly] {
823
+	if channel.flags.HasMode(modes.InviteOnly) {
838 824
 		nmc := invitee.NickCasefolded()
839 825
 		channel.stateMutex.Lock()
840 826
 		channel.lists[modes.InviteMask].Add(nmc)
@@ -850,7 +836,7 @@ func (channel *Channel) Invite(invitee *Client, inviter *Client, rb *ResponseBuf
850 836
 	//TODO(dan): should inviter.server.name here be inviter.nickMaskString ?
851 837
 	rb.Add(nil, inviter.server.name, RPL_INVITING, invitee.nick, channel.name)
852 838
 	invitee.Send(nil, inviter.nickMaskString, "INVITE", invitee.nick, channel.name)
853
-	if invitee.flags[modes.Away] {
839
+	if invitee.HasMode(modes.Away) {
854 840
 		rb.Add(nil, inviter.server.name, RPL_AWAY, invitee.nick, invitee.awayMessage)
855 841
 	}
856 842
 }

+ 2
- 2
irc/chanserv.go 파일 보기

@@ -199,7 +199,7 @@ func csOpHandler(server *Server, client *Client, command, params string, rb *Res
199 199
 	if client == target {
200 200
 		givenMode = modes.ChannelFounder
201 201
 	}
202
-	change := channelInfo.applyModeMemberNoMutex(target, givenMode, modes.Add, client.NickCasefolded(), rb)
202
+	change := channelInfo.applyModeToMember(target, givenMode, modes.Add, client.NickCasefolded(), rb)
203 203
 	if change != nil {
204 204
 		//TODO(dan): we should change the name of String and make it return a slice here
205 205
 		//TODO(dan): unify this code with code in modes.go
@@ -260,7 +260,7 @@ func csRegisterHandler(server *Server, client *Client, command, params string, r
260 260
 	server.snomasks.Send(sno.LocalChannels, fmt.Sprintf(ircfmt.Unescape("Channel registered $c[grey][$r%s$c[grey]] by $c[grey][$r%s$c[grey]]"), channelName, client.nickMaskString))
261 261
 
262 262
 	// give them founder privs
263
-	change := channelInfo.applyModeMemberNoMutex(client, modes.ChannelFounder, modes.Add, client.NickCasefolded(), rb)
263
+	change := channelInfo.applyModeToMember(client, modes.ChannelFounder, modes.Add, client.NickCasefolded(), rb)
264 264
 	if change != nil {
265 265
 		//TODO(dan): we should change the name of String and make it return a slice here
266 266
 		//TODO(dan): unify this code with code in modes.go

+ 4
- 10
irc/client.go 파일 보기

@@ -50,7 +50,7 @@ type Client struct {
50 50
 	ctime              time.Time
51 51
 	exitedSnomaskSent  bool
52 52
 	fakelag            *Fakelag
53
-	flags              map[modes.Mode]bool
53
+	flags              *modes.ModeSet
54 54
 	hasQuit            bool
55 55
 	hops               int
56 56
 	hostname           string
@@ -98,7 +98,7 @@ func NewClient(server *Server, conn net.Conn, isTLS bool) *Client {
98 98
 		capVersion:     caps.Cap301,
99 99
 		channels:       make(ChannelSet),
100 100
 		ctime:          now,
101
-		flags:          make(map[modes.Mode]bool),
101
+		flags:          modes.NewModeSet(),
102 102
 		server:         server,
103 103
 		socket:         socket,
104 104
 		nick:           "*", // * is used until actual nick is given
@@ -109,7 +109,7 @@ func NewClient(server *Server, conn net.Conn, isTLS bool) *Client {
109 109
 
110 110
 	client.recomputeMaxlens()
111 111
 	if isTLS {
112
-		client.flags[modes.TLS] = true
112
+		client.SetMode(modes.TLS, true)
113 113
 
114 114
 		// error is not useful to us here anyways so we can ignore it
115 115
 		client.certfp, _ = client.socket.CertFP()
@@ -504,13 +504,7 @@ func (client *Client) HasRoleCapabs(capabs ...string) bool {
504 504
 
505 505
 // ModeString returns the mode string for this client.
506 506
 func (client *Client) ModeString() (str string) {
507
-	str = "+"
508
-
509
-	for flag := range client.flags {
510
-		str += flag.String()
511
-	}
512
-
513
-	return
507
+	return "+" + client.flags.String()
514 508
 }
515 509
 
516 510
 // Friends refers to clients that share a channel with this client.

+ 8
- 2
irc/config.go 파일 보기

@@ -21,6 +21,7 @@ import (
21 21
 	"github.com/oragono/oragono/irc/custime"
22 22
 	"github.com/oragono/oragono/irc/languages"
23 23
 	"github.com/oragono/oragono/irc/logger"
24
+	"github.com/oragono/oragono/irc/modes"
24 25
 	"github.com/oragono/oragono/irc/passwd"
25 26
 	"github.com/oragono/oragono/irc/utils"
26 27
 	"gopkg.in/yaml.v2"
@@ -352,7 +353,7 @@ type Oper struct {
352 353
 	WhoisLine string
353 354
 	Vhost     string
354 355
 	Pass      []byte
355
-	Modes     string
356
+	Modes     []modes.ModeChange
356 357
 }
357 358
 
358 359
 // Operators returns a map of operator configs from the given OperClass and config.
@@ -379,7 +380,12 @@ func (conf *Config) Operators(oc *map[string]OperClass) (map[string]Oper, error)
379 380
 		} else {
380 381
 			oper.WhoisLine = class.WhoisLine
381 382
 		}
382
-		oper.Modes = strings.TrimSpace(opConf.Modes)
383
+		modeStr := strings.TrimSpace(opConf.Modes)
384
+		modeChanges, unknownChanges := modes.ParseUserModeChanges(strings.Split(modeStr, " ")...)
385
+		if len(unknownChanges) > 0 {
386
+			return nil, fmt.Errorf("Could not load operator [%s] due to unknown modes %v", name, unknownChanges)
387
+		}
388
+		oper.Modes = modeChanges
383 389
 
384 390
 		// successful, attach to list of opers
385 391
 		operators[name] = oper

+ 1
- 5
irc/gateways.go 파일 보기

@@ -81,11 +81,7 @@ func (client *Client) ApplyProxiedIP(proxiedIP string, tls bool) (exiting bool)
81 81
 
82 82
 	// set tls info
83 83
 	client.certfp = ""
84
-	if tls {
85
-		client.flags[modes.TLS] = true
86
-	} else {
87
-		delete(client.flags, modes.TLS)
88
-	}
84
+	client.SetMode(modes.TLS, tls)
89 85
 
90 86
 	return false
91 87
 }

+ 6
- 24
irc/getters.go 파일 보기

@@ -188,9 +188,12 @@ func (client *Client) SetPreregNick(preregNick string) {
188 188
 }
189 189
 
190 190
 func (client *Client) HasMode(mode modes.Mode) bool {
191
-	client.stateMutex.RLock()
192
-	defer client.stateMutex.RUnlock()
193
-	return client.flags[mode]
191
+	// client.flags has its own synch
192
+	return client.flags.HasMode(mode)
193
+}
194
+
195
+func (client *Client) SetMode(mode modes.Mode, on bool) bool {
196
+	return client.flags.SetMode(mode, on)
194 197
 }
195 198
 
196 199
 func (client *Client) Channels() (result []*Channel) {
@@ -260,29 +263,8 @@ func (channel *Channel) setKey(key string) {
260 263
 	channel.key = key
261 264
 }
262 265
 
263
-func (channel *Channel) HasMode(mode modes.Mode) bool {
264
-	channel.stateMutex.RLock()
265
-	defer channel.stateMutex.RUnlock()
266
-	return channel.flags[mode]
267
-}
268
-
269 266
 func (channel *Channel) Founder() string {
270 267
 	channel.stateMutex.RLock()
271 268
 	defer channel.stateMutex.RUnlock()
272 269
 	return channel.registeredFounder
273 270
 }
274
-
275
-// set a channel mode, return whether it was already set
276
-func (channel *Channel) setMode(mode modes.Mode, enable bool) (already bool) {
277
-	channel.stateMutex.Lock()
278
-	already = (channel.flags[mode] == enable)
279
-	if !already {
280
-		if enable {
281
-			channel.flags[mode] = true
282
-		} else {
283
-			delete(channel.flags, mode)
284
-		}
285
-	}
286
-	channel.stateMutex.Unlock()
287
-	return
288
-}

+ 28
- 45
irc/handlers.go 파일 보기

@@ -405,15 +405,11 @@ func awayHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
405 405
 		}
406 406
 	}
407 407
 
408
-	if isAway {
409
-		client.flags[modes.Away] = true
410
-	} else {
411
-		delete(client.flags, modes.Away)
412
-	}
408
+	client.SetMode(modes.Away, isAway)
413 409
 	client.awayMessage = text
414 410
 
415 411
 	var op modes.ModeOp
416
-	if client.flags[modes.Away] {
412
+	if isAway {
417 413
 		op = modes.Add
418 414
 		rb.Add(nil, server.name, RPL_NOWAWAY, client.nick, client.t("You have been marked as being away"))
419 415
 	} else {
@@ -429,7 +425,7 @@ func awayHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
429 425
 
430 426
 	// dispatch away-notify
431 427
 	for friend := range client.Friends(caps.AwayNotify) {
432
-		if client.flags[modes.Away] {
428
+		if isAway {
433 429
 			friend.SendFromClient("", client, nil, "AWAY", client.awayMessage)
434 430
 		} else {
435 431
 			friend.SendFromClient("", client, nil, "AWAY")
@@ -777,7 +773,7 @@ Get an explanation of <argument>, or "index" for a list of help topics.`), rb)
777 773
 
778 774
 	// handle index
779 775
 	if argument == "index" {
780
-		if client.flags[modes.Operator] {
776
+		if client.HasMode(modes.Operator) {
781 777
 			client.sendHelp("HELP", GetHelpIndex(client.languages, HelpIndexOpers), rb)
782 778
 		} else {
783 779
 			client.sendHelp("HELP", GetHelpIndex(client.languages, HelpIndex), rb)
@@ -787,7 +783,7 @@ Get an explanation of <argument>, or "index" for a list of help topics.`), rb)
787 783
 
788 784
 	helpHandler, exists := Help[argument]
789 785
 
790
-	if exists && (!helpHandler.oper || (helpHandler.oper && client.flags[modes.Operator])) {
786
+	if exists && (!helpHandler.oper || (helpHandler.oper && client.HasMode(modes.Operator))) {
791 787
 		if helpHandler.textGenerator != nil {
792 788
 			client.sendHelp(strings.ToUpper(argument), client.t(helpHandler.textGenerator(client)), rb)
793 789
 		} else {
@@ -1257,9 +1253,10 @@ func listHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
1257 1253
 		}
1258 1254
 	}
1259 1255
 
1256
+	clientIsOp := client.HasMode(modes.Operator)
1260 1257
 	if len(channels) == 0 {
1261 1258
 		for _, channel := range server.channels.Channels() {
1262
-			if !client.flags[modes.Operator] && channel.flags[modes.Secret] {
1259
+			if !clientIsOp && channel.flags.HasMode(modes.Secret) {
1263 1260
 				continue
1264 1261
 			}
1265 1262
 			if matcher.Matches(channel) {
@@ -1268,14 +1265,14 @@ func listHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
1268 1265
 		}
1269 1266
 	} else {
1270 1267
 		// limit regular users to only listing one channel
1271
-		if !client.flags[modes.Operator] {
1268
+		if !clientIsOp {
1272 1269
 			channels = channels[:1]
1273 1270
 		}
1274 1271
 
1275 1272
 		for _, chname := range channels {
1276 1273
 			casefoldedChname, err := CasefoldChannel(chname)
1277 1274
 			channel := server.channels.Get(casefoldedChname)
1278
-			if err != nil || channel == nil || (!client.flags[modes.Operator] && channel.flags[modes.Secret]) {
1275
+			if err != nil || channel == nil || (!clientIsOp && channel.flags.HasMode(modes.Secret)) {
1279 1276
 				if len(chname) > 0 {
1280 1277
 					rb.Add(nil, server.name, ERR_NOSUCHCHANNEL, client.nick, chname, client.t("No such channel"))
1281 1278
 				}
@@ -1329,7 +1326,7 @@ func cmodeHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Res
1329 1326
 	if 1 < len(msg.Params) {
1330 1327
 		// parse out real mode changes
1331 1328
 		params := msg.Params[1:]
1332
-		changes, unknown := ParseChannelModeChanges(params...)
1329
+		changes, unknown := modes.ParseChannelModeChanges(params...)
1333 1330
 
1334 1331
 		// alert for unknown mode changes
1335 1332
 		for char := range unknown {
@@ -1415,14 +1412,14 @@ func umodeHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Res
1415 1412
 		}
1416 1413
 
1417 1414
 		// apply mode changes
1418
-		applied = target.applyUserModeChanges(msg.Command == "SAMODE", changes)
1415
+		applied = ApplyUserModeChanges(client, changes, msg.Command == "SAMODE")
1419 1416
 	}
1420 1417
 
1421 1418
 	if len(applied) > 0 {
1422 1419
 		rb.Add(nil, client.nickMaskString, "MODE", targetNick, applied.String())
1423 1420
 	} else if hasPrivs {
1424 1421
 		rb.Add(nil, target.nickMaskString, RPL_UMODEIS, targetNick, target.ModeString())
1425
-		if client.flags[modes.LocalOperator] || client.flags[modes.Operator] {
1422
+		if client.HasMode(modes.LocalOperator) || client.HasMode(modes.Operator) {
1426 1423
 			masks := server.snomasks.String(client)
1427 1424
 			if 0 < len(masks) {
1428 1425
 				rb.Add(nil, target.nickMaskString, RPL_SNOMASKIS, targetNick, masks, client.t("Server notice masks"))
@@ -1670,7 +1667,7 @@ func noticeHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re
1670 1667
 			msgid := server.generateMessageID()
1671 1668
 			// restrict messages appropriately when +R is set
1672 1669
 			// intentionally make the sending user think the message went through fine
1673
-			if !user.flags[modes.RegisteredOnly] || client.registered {
1670
+			if !user.HasMode(modes.RegisteredOnly) || client.LoggedIntoAccount() {
1674 1671
 				user.SendSplitMsgFromClient(msgid, client, clientOnlyTags, "NOTICE", user.nick, splitMsg)
1675 1672
 			}
1676 1673
 			if client.capabilities.Has(caps.EchoMessage) {
@@ -1731,7 +1728,7 @@ func operHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
1731 1728
 		rb.Add(nil, server.name, ERR_PASSWDMISMATCH, client.nick, client.t("Password incorrect"))
1732 1729
 		return true
1733 1730
 	}
1734
-	if client.flags[modes.Operator] == true {
1731
+	if client.HasMode(modes.Operator) == true {
1735 1732
 		rb.Add(nil, server.name, ERR_UNKNOWNERROR, "OPER", client.t("You're already opered-up!"))
1736 1733
 		return false
1737 1734
 	}
@@ -1760,37 +1757,23 @@ func operHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
1760 1757
 		client.updateNickMask("")
1761 1758
 	}
1762 1759
 
1763
-	// set new modes
1764
-	var applied modes.ModeChanges
1765
-	if 0 < len(oper.Modes) {
1766
-		modeChanges, unknownChanges := modes.ParseUserModeChanges(strings.Split(oper.Modes, " ")...)
1767
-		applied = client.applyUserModeChanges(true, modeChanges)
1768
-		if 0 < len(unknownChanges) {
1769
-			var runes string
1770
-			for r := range unknownChanges {
1771
-				runes += string(r)
1772
-			}
1773
-			rb.Notice(fmt.Sprintf(client.t("Could not apply mode changes: +%s"), runes))
1774
-		}
1760
+	// set new modes: modes.Operator, plus anything specified in the config
1761
+	modeChanges := make([]modes.ModeChange, len(oper.Modes)+1)
1762
+	modeChanges[0] = modes.ModeChange{
1763
+		Mode: modes.Operator,
1764
+		Op:   modes.Add,
1775 1765
 	}
1766
+	copy(modeChanges[1:], oper.Modes)
1767
+	applied := ApplyUserModeChanges(client, modeChanges, true)
1776 1768
 
1777 1769
 	rb.Add(nil, server.name, RPL_YOUREOPER, client.nick, client.t("You are now an IRC operator"))
1778
-
1779
-	applied = append(applied, modes.ModeChange{
1780
-		Mode: modes.Operator,
1781
-		Op:   modes.Add,
1782
-	})
1783 1770
 	rb.Add(nil, server.name, "MODE", client.nick, applied.String())
1784 1771
 
1785 1772
 	server.snomasks.Send(sno.LocalOpers, fmt.Sprintf(ircfmt.Unescape("Client opered up $c[grey][$r%s$c[grey], $r%s$c[grey]]"), client.nickMaskString, client.operName))
1786 1773
 
1787
-	// increase oper count
1788
-	server.stats.ChangeOperators(1)
1789
-
1790 1774
 	// client may now be unthrottled by the fakelag system
1791 1775
 	client.resetFakelag()
1792 1776
 
1793
-	client.flags[modes.Operator] = true
1794 1777
 	return false
1795 1778
 }
1796 1779
 
@@ -1905,13 +1888,13 @@ func privmsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *R
1905 1888
 			msgid := server.generateMessageID()
1906 1889
 			// restrict messages appropriately when +R is set
1907 1890
 			// intentionally make the sending user think the message went through fine
1908
-			if !user.flags[modes.RegisteredOnly] || client.registered {
1891
+			if !user.HasMode(modes.RegisteredOnly) || client.LoggedIntoAccount() {
1909 1892
 				user.SendSplitMsgFromClient(msgid, client, clientOnlyTags, "PRIVMSG", user.nick, splitMsg)
1910 1893
 			}
1911 1894
 			if client.capabilities.Has(caps.EchoMessage) {
1912 1895
 				rb.AddSplitMessageFromClient(msgid, client, clientOnlyTags, "PRIVMSG", user.nick, splitMsg)
1913 1896
 			}
1914
-			if user.flags[modes.Away] {
1897
+			if user.HasMode(modes.Away) {
1915 1898
 				//TODO(dan): possibly implement cooldown of away notifications to users
1916 1899
 				rb.Add(nil, server.name, RPL_AWAY, user.nick, user.awayMessage)
1917 1900
 			}
@@ -2159,7 +2142,7 @@ func tagmsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re
2159 2142
 			if client.capabilities.Has(caps.EchoMessage) {
2160 2143
 				rb.AddFromClient(msgid, client, clientOnlyTags, "TAGMSG", user.nick)
2161 2144
 			}
2162
-			if user.flags[modes.Away] {
2145
+			if user.HasMode(modes.Away) {
2163 2146
 				//TODO(dan): possibly implement cooldown of away notifications to users
2164 2147
 				rb.Add(nil, server.name, RPL_AWAY, user.nick, user.awayMessage)
2165 2148
 			}
@@ -2355,10 +2338,10 @@ func userhostHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *
2355 2338
 
2356 2339
 		var isOper, isAway string
2357 2340
 
2358
-		if target.flags[modes.Operator] {
2341
+		if target.HasMode(modes.Operator) {
2359 2342
 			isOper = "*"
2360 2343
 		}
2361
-		if target.flags[modes.Away] {
2344
+		if target.HasMode(modes.Away) {
2362 2345
 			isAway = "-"
2363 2346
 		} else {
2364 2347
 			isAway = "+"
@@ -2399,7 +2382,7 @@ func webircHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re
2399 2382
 			lkey := strings.ToLower(key)
2400 2383
 			if lkey == "tls" || lkey == "secure" {
2401 2384
 				// only accept "tls" flag if the gateway's connection to us is secure as well
2402
-				if client.flags[modes.TLS] || utils.AddrIsLocal(client.socket.conn.RemoteAddr()) {
2385
+				if client.HasMode(modes.TLS) || utils.AddrIsLocal(client.socket.conn.RemoteAddr()) {
2403 2386
 					secure = true
2404 2387
 				}
2405 2388
 			}
@@ -2488,7 +2471,7 @@ func whoisHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Res
2488 2471
 		return false
2489 2472
 	}
2490 2473
 
2491
-	if client.flags[modes.Operator] {
2474
+	if client.HasMode(modes.Operator) {
2492 2475
 		masks := strings.Split(masksString, ",")
2493 2476
 		for _, mask := range masks {
2494 2477
 			casefoldedMask, err := Casefold(mask)

+ 33
- 111
irc/modes.go 파일 보기

@@ -21,8 +21,8 @@ var (
21 21
 	}
22 22
 )
23 23
 
24
-// applyUserModeChanges applies the given changes, and returns the applied changes.
25
-func (client *Client) applyUserModeChanges(force bool, changes modes.ModeChanges) modes.ModeChanges {
24
+// ApplyUserModeChanges applies the given changes, and returns the applied changes.
25
+func ApplyUserModeChanges(client *Client, changes modes.ModeChanges, force bool) modes.ModeChanges {
26 26
 	applied := make(modes.ModeChanges, 0)
27 27
 
28 28
 	for _, change := range changes {
@@ -34,36 +34,28 @@ func (client *Client) applyUserModeChanges(force bool, changes modes.ModeChanges
34 34
 					continue
35 35
 				}
36 36
 
37
-				if client.flags[change.Mode] {
38
-					continue
39
-				}
40
-
41
-				if change.Mode == modes.Invisible {
42
-					client.server.stats.ChangeInvisible(1)
37
+				if changed := client.SetMode(change.Mode, true); changed {
38
+					if change.Mode == modes.Invisible {
39
+						client.server.stats.ChangeInvisible(1)
40
+					} else if change.Mode == modes.Operator || change.Mode == modes.LocalOperator {
41
+						client.server.stats.ChangeOperators(1)
42
+					}
43
+					applied = append(applied, change)
43 44
 				}
44 45
 
45
-				client.flags[change.Mode] = true
46
-				applied = append(applied, change)
47
-
48 46
 			case modes.Remove:
49
-				if !client.flags[change.Mode] {
50
-					continue
51
-				}
52
-
53
-				if change.Mode == modes.Invisible {
54
-					client.server.stats.ChangeInvisible(-1)
55
-				}
56
-
57
-				if change.Mode == modes.Operator || change.Mode == modes.LocalOperator {
58
-					client.server.stats.ChangeOperators(-1)
47
+				if changed := client.SetMode(change.Mode, false); changed {
48
+					if change.Mode == modes.Invisible {
49
+						client.server.stats.ChangeInvisible(-1)
50
+					} else if change.Mode == modes.Operator || change.Mode == modes.LocalOperator {
51
+						client.server.stats.ChangeOperators(-1)
52
+					}
53
+					applied = append(applied, change)
59 54
 				}
60
-
61
-				delete(client.flags, change.Mode)
62
-				applied = append(applied, change)
63 55
 			}
64 56
 
65 57
 		case modes.ServerNotice:
66
-			if !client.flags[modes.Operator] {
58
+			if !client.HasMode(modes.Operator) {
67 59
 				continue
68 60
 			}
69 61
 			var masks []sno.Mask
@@ -101,7 +93,7 @@ func ParseDefaultChannelModes(config *Config) modes.Modes {
101 93
 		return DefaultChannelModes
102 94
 	}
103 95
 	modeChangeStrings := strings.Split(strings.TrimSpace(*config.Channels.DefaultModes), " ")
104
-	modeChanges, _ := ParseChannelModeChanges(modeChangeStrings...)
96
+	modeChanges, _ := modes.ParseChannelModeChanges(modeChangeStrings...)
105 97
 	defaultChannelModes := make(modes.Modes, 0)
106 98
 	for _, modeChange := range modeChanges {
107 99
 		if modeChange.Op == modes.Add {
@@ -111,83 +103,6 @@ func ParseDefaultChannelModes(config *Config) modes.Modes {
111 103
 	return defaultChannelModes
112 104
 }
113 105
 
114
-// ParseChannelModeChanges returns the valid changes, and the list of unknown chars.
115
-func ParseChannelModeChanges(params ...string) (modes.ModeChanges, map[rune]bool) {
116
-	changes := make(modes.ModeChanges, 0)
117
-	unknown := make(map[rune]bool)
118
-
119
-	op := modes.List
120
-
121
-	if 0 < len(params) {
122
-		modeArg := params[0]
123
-		skipArgs := 1
124
-
125
-		for _, mode := range modeArg {
126
-			if mode == '-' || mode == '+' {
127
-				op = modes.ModeOp(mode)
128
-				continue
129
-			}
130
-			change := modes.ModeChange{
131
-				Mode: modes.Mode(mode),
132
-				Op:   op,
133
-			}
134
-
135
-			// put arg into modechange if needed
136
-			switch modes.Mode(mode) {
137
-			case modes.BanMask, modes.ExceptMask, modes.InviteMask:
138
-				if len(params) > skipArgs {
139
-					change.Arg = params[skipArgs]
140
-					skipArgs++
141
-				} else {
142
-					change.Op = modes.List
143
-				}
144
-			case modes.ChannelFounder, modes.ChannelAdmin, modes.ChannelOperator, modes.Halfop, modes.Voice:
145
-				if len(params) > skipArgs {
146
-					change.Arg = params[skipArgs]
147
-					skipArgs++
148
-				} else {
149
-					continue
150
-				}
151
-			case modes.Key, modes.UserLimit:
152
-				// don't require value when removing
153
-				if change.Op == modes.Add {
154
-					if len(params) > skipArgs {
155
-						change.Arg = params[skipArgs]
156
-						skipArgs++
157
-					} else {
158
-						continue
159
-					}
160
-				}
161
-			}
162
-
163
-			var isKnown bool
164
-			for _, supportedMode := range modes.SupportedChannelModes {
165
-				if rune(supportedMode) == mode {
166
-					isKnown = true
167
-					break
168
-				}
169
-			}
170
-			for _, supportedMode := range modes.ChannelPrivModes {
171
-				if rune(supportedMode) == mode {
172
-					isKnown = true
173
-					break
174
-				}
175
-			}
176
-			if mode == rune(modes.Voice) {
177
-				isKnown = true
178
-			}
179
-			if !isKnown {
180
-				unknown[mode] = true
181
-				continue
182
-			}
183
-
184
-			changes = append(changes, change)
185
-		}
186
-	}
187
-
188
-	return changes, unknown
189
-}
190
-
191 106
 // ApplyChannelModeChanges applies a given set of mode changes.
192 107
 func (channel *Channel) ApplyChannelModeChanges(client *Client, isSamode bool, changes modes.ModeChanges, rb *ResponseBuffer) modes.ModeChanges {
193 108
 	// so we only output one warning for each list type when full
@@ -208,15 +123,17 @@ func (channel *Channel) ApplyChannelModeChanges(client *Client, isSamode bool, c
208 123
 		}
209 124
 		switch change.Mode {
210 125
 		case modes.ChannelFounder, modes.ChannelAdmin, modes.ChannelOperator, modes.Halfop, modes.Voice:
211
-			// Admins can't give other people Admin or remove it from others
212
-			if change.Mode == modes.ChannelAdmin {
213
-				return false
214
-			}
126
+			// List on these modes is a no-op anyway
215 127
 			if change.Op == modes.List {
216 128
 				return true
217 129
 			}
218 130
 			cfarg, _ := CasefoldName(change.Arg)
219
-			if change.Op == modes.Remove && cfarg == client.nickCasefolded {
131
+			isSelfChange := cfarg == client.NickCasefolded()
132
+			// Admins can't give other people Admin or remove it from others
133
+			if change.Mode == modes.ChannelAdmin && !isSelfChange {
134
+				return false
135
+			}
136
+			if change.Op == modes.Remove && isSelfChange {
220 137
 				// "There is no restriction, however, on anyone `deopping' themselves"
221 138
 				// <https://tools.ietf.org/html/rfc2812#section-3.1.5>
222 139
 				return true
@@ -299,8 +216,7 @@ func (channel *Channel) ApplyChannelModeChanges(client *Client, isSamode bool, c
299 216
 				continue
300 217
 			}
301 218
 
302
-			already := channel.setMode(change.Mode, change.Op == modes.Add)
303
-			if !already {
219
+			if changed := channel.flags.SetMode(change.Mode, change.Op == modes.Add); changed {
304 220
 				applied = append(applied, change)
305 221
 			}
306 222
 
@@ -309,7 +225,13 @@ func (channel *Channel) ApplyChannelModeChanges(client *Client, isSamode bool, c
309 225
 				continue
310 226
 			}
311 227
 
312
-			change := channel.applyModeMemberNoMutex(client, change.Mode, change.Op, change.Arg, rb)
228
+			nick := change.Arg
229
+			if nick == "" {
230
+				rb.Add(nil, client.server.name, ERR_NEEDMOREPARAMS, "MODE", client.t("Not enough parameters"))
231
+				return nil
232
+			}
233
+
234
+			change := channel.applyModeToMember(client, change.Mode, change.Op, nick, rb)
313 235
 			if change != nil {
314 236
 				applied = append(applied, *change)
315 237
 			}

+ 136
- 13
irc/modes/modes.go 파일 보기

@@ -7,6 +7,7 @@ package modes
7 7
 
8 8
 import (
9 9
 	"strings"
10
+	"sync"
10 11
 )
11 12
 
12 13
 var (
@@ -247,34 +248,156 @@ func ParseUserModeChanges(params ...string) (ModeChanges, map[rune]bool) {
247 248
 	return changes, unknown
248 249
 }
249 250
 
251
+// ParseChannelModeChanges returns the valid changes, and the list of unknown chars.
252
+func ParseChannelModeChanges(params ...string) (ModeChanges, map[rune]bool) {
253
+	changes := make(ModeChanges, 0)
254
+	unknown := make(map[rune]bool)
255
+
256
+	op := List
257
+
258
+	if 0 < len(params) {
259
+		modeArg := params[0]
260
+		skipArgs := 1
261
+
262
+		for _, mode := range modeArg {
263
+			if mode == '-' || mode == '+' {
264
+				op = ModeOp(mode)
265
+				continue
266
+			}
267
+			change := ModeChange{
268
+				Mode: Mode(mode),
269
+				Op:   op,
270
+			}
271
+
272
+			// put arg into modechange if needed
273
+			switch Mode(mode) {
274
+			case BanMask, ExceptMask, InviteMask:
275
+				if len(params) > skipArgs {
276
+					change.Arg = params[skipArgs]
277
+					skipArgs++
278
+				} else {
279
+					change.Op = List
280
+				}
281
+			case ChannelFounder, ChannelAdmin, ChannelOperator, Halfop, Voice:
282
+				if len(params) > skipArgs {
283
+					change.Arg = params[skipArgs]
284
+					skipArgs++
285
+				} else {
286
+					continue
287
+				}
288
+			case Key, UserLimit:
289
+				// don't require value when removing
290
+				if change.Op == Add {
291
+					if len(params) > skipArgs {
292
+						change.Arg = params[skipArgs]
293
+						skipArgs++
294
+					} else {
295
+						continue
296
+					}
297
+				}
298
+			}
299
+
300
+			var isKnown bool
301
+			for _, supportedMode := range SupportedChannelModes {
302
+				if rune(supportedMode) == mode {
303
+					isKnown = true
304
+					break
305
+				}
306
+			}
307
+			for _, supportedMode := range ChannelPrivModes {
308
+				if rune(supportedMode) == mode {
309
+					isKnown = true
310
+					break
311
+				}
312
+			}
313
+			if mode == rune(Voice) {
314
+				isKnown = true
315
+			}
316
+			if !isKnown {
317
+				unknown[mode] = true
318
+				continue
319
+			}
320
+
321
+			changes = append(changes, change)
322
+		}
323
+	}
324
+
325
+	return changes, unknown
326
+}
327
+
250 328
 // ModeSet holds a set of modes.
251
-type ModeSet map[Mode]bool
329
+type ModeSet struct {
330
+	sync.RWMutex // tier 0
331
+	modes        map[Mode]bool
332
+}
333
+
334
+// returns a pointer to a new ModeSet
335
+func NewModeSet() *ModeSet {
336
+	return &ModeSet{
337
+		modes: make(map[Mode]bool),
338
+	}
339
+}
340
+
341
+// test whether `mode` is set
342
+func (set *ModeSet) HasMode(mode Mode) bool {
343
+	set.RLock()
344
+	defer set.RUnlock()
345
+	return set.modes[mode]
346
+}
347
+
348
+// set `mode` to be on or off, return whether the value actually changed
349
+func (set *ModeSet) SetMode(mode Mode, on bool) (applied bool) {
350
+	set.Lock()
351
+	defer set.Unlock()
352
+
353
+	previouslyOn := set.modes[mode]
354
+	needsApply := (on != previouslyOn)
355
+	if on && needsApply {
356
+		set.modes[mode] = true
357
+	} else if !on && needsApply {
358
+		delete(set.modes, mode)
359
+	}
360
+	return needsApply
361
+}
362
+
363
+// return the modes in the set as a slice
364
+func (set *ModeSet) AllModes() (result []Mode) {
365
+	set.RLock()
366
+	defer set.RUnlock()
367
+
368
+	for mode := range set.modes {
369
+		result = append(result, mode)
370
+	}
371
+	return
372
+}
252 373
 
253 374
 // String returns the modes in this set.
254
-func (set ModeSet) String() string {
255
-	if len(set) == 0 {
375
+func (set *ModeSet) String() string {
376
+	set.RLock()
377
+	defer set.RUnlock()
378
+
379
+	if len(set.modes) == 0 {
256 380
 		return ""
257 381
 	}
258
-	strs := make([]string, len(set))
259
-	index := 0
260
-	for mode := range set {
261
-		strs[index] = mode.String()
262
-		index++
382
+	var result []byte
383
+	for mode := range set.modes {
384
+		result = append(result, mode.String()...)
263 385
 	}
264
-	return strings.Join(strs, "")
386
+	return string(result)
265 387
 }
266 388
 
267 389
 // Prefixes returns a list of prefixes for the given set of channel modes.
268
-func (set ModeSet) Prefixes(isMultiPrefix bool) string {
269
-	var prefixes string
390
+func (set *ModeSet) Prefixes(isMultiPrefix bool) (prefixes string) {
391
+	set.RLock()
392
+	defer set.RUnlock()
270 393
 
271 394
 	// add prefixes in order from highest to lowest privs
272 395
 	for _, mode := range ChannelPrivModes {
273
-		if set[mode] {
396
+		if set.modes[mode] {
274 397
 			prefixes += ChannelModePrefixes[mode]
275 398
 		}
276 399
 	}
277
-	if set[Voice] {
400
+	if set.modes[Voice] {
278 401
 		prefixes += ChannelModePrefixes[Voice]
279 402
 	}
280 403
 

+ 37
- 0
irc/modes/modes_test.go 파일 보기

@@ -0,0 +1,37 @@
1
+// Copyright (c) 2018 Shivaram Lingamneni
2
+// released under the MIT license
3
+
4
+package modes
5
+
6
+import (
7
+	"reflect"
8
+	"testing"
9
+)
10
+
11
+func TestSetMode(t *testing.T) {
12
+	set := NewModeSet()
13
+
14
+	if applied := set.SetMode(Invisible, false); applied != false {
15
+		t.Errorf("all modes should be false by default")
16
+	}
17
+
18
+	if applied := set.SetMode(Invisible, true); applied != true {
19
+		t.Errorf("initial SetMode call should return true")
20
+	}
21
+
22
+	set.SetMode(Operator, true)
23
+
24
+	if applied := set.SetMode(Invisible, true); applied != false {
25
+		t.Errorf("redundant SetMode call should return false")
26
+	}
27
+
28
+	expected1 := []Mode{Invisible, Operator}
29
+	expected2 := []Mode{Operator, Invisible}
30
+	if allModes := set.AllModes(); !(reflect.DeepEqual(allModes, expected1) || reflect.DeepEqual(allModes, expected2)) {
31
+		t.Errorf("unexpected AllModes value: %v", allModes)
32
+	}
33
+
34
+	if modeString := set.String(); !(modeString == "io" || modeString == "oi") {
35
+		t.Errorf("unexpected modestring: %s", modeString)
36
+	}
37
+}

+ 3
- 3
irc/roleplay.go 파일 보기

@@ -35,7 +35,7 @@ func sendRoleplayMessage(server *Server, client *Client, source string, targetSt
35 35
 			return
36 36
 		}
37 37
 
38
-		if !channel.flags[modes.ChanRoleplaying] {
38
+		if !channel.flags.HasMode(modes.ChanRoleplaying) {
39 39
 			rb.Add(nil, client.server.name, ERR_CANNOTSENDRP, channel.name, client.t("Channel doesn't have roleplaying mode available"))
40 40
 			return
41 41
 		}
@@ -58,7 +58,7 @@ func sendRoleplayMessage(server *Server, client *Client, source string, targetSt
58 58
 			return
59 59
 		}
60 60
 
61
-		if !user.flags[modes.UserRoleplaying] {
61
+		if !user.HasMode(modes.UserRoleplaying) {
62 62
 			rb.Add(nil, client.server.name, ERR_CANNOTSENDRP, user.nick, client.t("User doesn't have roleplaying mode enabled"))
63 63
 			return
64 64
 		}
@@ -67,7 +67,7 @@ func sendRoleplayMessage(server *Server, client *Client, source string, targetSt
67 67
 		if client.capabilities.Has(caps.EchoMessage) {
68 68
 			rb.Add(nil, source, "PRIVMSG", user.nick, message)
69 69
 		}
70
-		if user.flags[modes.Away] {
70
+		if user.HasMode(modes.Away) {
71 71
 			//TODO(dan): possibly implement cooldown of away notifications to users
72 72
 			rb.Add(nil, server.name, RPL_AWAY, user.nick, user.awayMessage)
73 73
 		}

+ 8
- 8
irc/server.go 파일 보기

@@ -637,8 +637,8 @@ func (client *Client) WhoisChannelsNames(target *Client) []string {
637 637
 	var chstrs []string
638 638
 	for _, channel := range target.Channels() {
639 639
 		// channel is secret and the target can't see it
640
-		if !client.flags[modes.Operator] {
641
-			if (target.HasMode(modes.Invisible) || channel.HasMode(modes.Secret)) && !channel.hasClient(client) {
640
+		if !client.HasMode(modes.Operator) {
641
+			if (target.HasMode(modes.Invisible) || channel.flags.HasMode(modes.Secret)) && !channel.hasClient(client) {
642 642
 				continue
643 643
 			}
644 644
 		}
@@ -660,16 +660,16 @@ func (client *Client) getWhoisOf(target *Client, rb *ResponseBuffer) {
660 660
 	if target.class != nil {
661 661
 		rb.Add(nil, client.server.name, RPL_WHOISOPERATOR, client.nick, target.nick, target.whoisLine)
662 662
 	}
663
-	if client.flags[modes.Operator] || client == target {
663
+	if client.HasMode(modes.Operator) || client == target {
664 664
 		rb.Add(nil, client.server.name, RPL_WHOISACTUALLY, client.nick, target.nick, fmt.Sprintf("%s@%s", target.username, utils.LookupHostname(target.IPString())), target.IPString(), client.t("Actual user@host, Actual IP"))
665 665
 	}
666
-	if target.flags[modes.TLS] {
666
+	if target.HasMode(modes.TLS) {
667 667
 		rb.Add(nil, client.server.name, RPL_WHOISSECURE, client.nick, target.nick, client.t("is using a secure connection"))
668 668
 	}
669 669
 	if target.LoggedIntoAccount() {
670 670
 		rb.Add(nil, client.server.name, RPL_WHOISACCOUNT, client.nick, target.AccountName(), client.t("is logged in as"))
671 671
 	}
672
-	if target.flags[modes.Bot] {
672
+	if target.HasMode(modes.Bot) {
673 673
 		rb.Add(nil, client.server.name, RPL_WHOISBOT, client.nick, target.nick, ircfmt.Unescape(fmt.Sprintf(client.t("is a $bBot$b on %s"), client.server.networkName)))
674 674
 	}
675 675
 
@@ -682,7 +682,7 @@ func (client *Client) getWhoisOf(target *Client, rb *ResponseBuffer) {
682 682
 		rb.Add(nil, client.server.name, RPL_WHOISLANGUAGE, params...)
683 683
 	}
684 684
 
685
-	if target.certfp != "" && (client.flags[modes.Operator] || client == target) {
685
+	if target.certfp != "" && (client.HasMode(modes.Operator) || client == target) {
686 686
 		rb.Add(nil, client.server.name, RPL_WHOISCERTFP, client.nick, target.nick, fmt.Sprintf(client.t("has client certificate fingerprint %s"), target.certfp))
687 687
 	}
688 688
 	rb.Add(nil, client.server.name, RPL_WHOISIDLE, client.nick, target.nick, strconv.FormatUint(target.IdleSeconds(), 10), strconv.FormatInt(target.SignonTime(), 10), client.t("seconds idle, signon time"))
@@ -713,7 +713,7 @@ func (target *Client) rplWhoReply(channel *Channel, client *Client, rb *Response
713 713
 
714 714
 func whoChannel(client *Client, channel *Channel, friends ClientSet, rb *ResponseBuffer) {
715 715
 	for _, member := range channel.Members() {
716
-		if !client.flags[modes.Invisible] || friends[client] {
716
+		if !client.HasMode(modes.Invisible) || friends[client] {
717 717
 			client.rplWhoReply(channel, member, rb)
718 718
 		}
719 719
 	}
@@ -1209,7 +1209,7 @@ func (matcher *elistMatcher) Matches(channel *Channel) bool {
1209 1209
 func (target *Client) RplList(channel *Channel, rb *ResponseBuffer) {
1210 1210
 	// get the correct number of channel members
1211 1211
 	var memberCount int
1212
-	if target.flags[modes.Operator] || channel.hasClient(target) {
1212
+	if target.HasMode(modes.Operator) || channel.hasClient(target) {
1213 1213
 		memberCount = len(channel.Members())
1214 1214
 	} else {
1215 1215
 		for _, member := range channel.Members() {

+ 3
- 12
irc/types.go 파일 보기

@@ -26,11 +26,11 @@ func (clients ClientSet) Has(client *Client) bool {
26 26
 }
27 27
 
28 28
 // MemberSet is a set of members with modes.
29
-type MemberSet map[*Client]modes.ModeSet
29
+type MemberSet map[*Client]*modes.ModeSet
30 30
 
31 31
 // Add adds the given client to this set.
32 32
 func (members MemberSet) Add(member *Client) {
33
-	members[member] = make(modes.ModeSet)
33
+	members[member] = modes.NewModeSet()
34 34
 }
35 35
 
36 36
 // Remove removes the given client from this set.
@@ -44,19 +44,10 @@ func (members MemberSet) Has(member *Client) bool {
44 44
 	return ok
45 45
 }
46 46
 
47
-// HasMode returns true if the given client is in this set with the given mode.
48
-func (members MemberSet) HasMode(member *Client, mode modes.Mode) bool {
49
-	modes, ok := members[member]
50
-	if !ok {
51
-		return false
52
-	}
53
-	return modes[mode]
54
-}
55
-
56 47
 // AnyHasMode returns true if any of our clients has the given mode.
57 48
 func (members MemberSet) AnyHasMode(mode modes.Mode) bool {
58 49
 	for _, modes := range members {
59
-		if modes[mode] {
50
+		if modes.HasMode(mode) {
60 51
 			return true
61 52
 		}
62 53
 	}

Loading…
취소
저장