浏览代码

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
 	git submodule update --init
12
 	git submodule update --init
13
 
13
 
14
 test:
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
 
20
 
21
 // Channel represents a channel that clients can join.
21
 // Channel represents a channel that clients can join.
22
 type Channel struct {
22
 type Channel struct {
23
-	flags             modes.ModeSet
23
+	flags             *modes.ModeSet
24
 	lists             map[modes.Mode]*UserMaskSet
24
 	lists             map[modes.Mode]*UserMaskSet
25
 	key               string
25
 	key               string
26
 	members           MemberSet
26
 	members           MemberSet
51
 
51
 
52
 	channel := &Channel{
52
 	channel := &Channel{
53
 		createdTime: time.Now(), // may be overwritten by applyRegInfo
53
 		createdTime: time.Now(), // may be overwritten by applyRegInfo
54
-		flags:       make(modes.ModeSet),
54
+		flags:       modes.NewModeSet(),
55
 		lists: map[modes.Mode]*UserMaskSet{
55
 		lists: map[modes.Mode]*UserMaskSet{
56
 			modes.BanMask:    NewUserMaskSet(),
56
 			modes.BanMask:    NewUserMaskSet(),
57
 			modes.ExceptMask: NewUserMaskSet(),
57
 			modes.ExceptMask: NewUserMaskSet(),
68
 		channel.applyRegInfo(regInfo)
68
 		channel.applyRegInfo(regInfo)
69
 	} else {
69
 	} else {
70
 		for _, mode := range s.DefaultChannelModes() {
70
 		for _, mode := range s.DefaultChannelModes() {
71
-			channel.flags[mode] = true
71
+			channel.flags.SetMode(mode, true)
72
 		}
72
 		}
73
 	}
73
 	}
74
 
74
 
87
 	channel.key = chanReg.Key
87
 	channel.key = chanReg.Key
88
 
88
 
89
 	for _, mode := range chanReg.Modes {
89
 	for _, mode := range chanReg.Modes {
90
-		channel.flags[mode] = true
90
+		channel.flags.SetMode(mode, true)
91
 	}
91
 	}
92
 	for _, mask := range chanReg.Banlist {
92
 	for _, mask := range chanReg.Banlist {
93
 		channel.lists[modes.BanMask].Add(mask)
93
 		channel.lists[modes.BanMask].Add(mask)
120
 
120
 
121
 	if includeFlags&IncludeModes != 0 {
121
 	if includeFlags&IncludeModes != 0 {
122
 		info.Key = channel.key
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
 	if includeFlags&IncludeLists != 0 {
126
 	if includeFlags&IncludeLists != 0 {
225
 	channel.stateMutex.RLock()
223
 	channel.stateMutex.RLock()
226
 	defer channel.stateMutex.RUnlock()
224
 	defer channel.stateMutex.RUnlock()
227
 
225
 
226
+	clientModes := channel.members[client]
227
+
228
 	// get voice, since it's not a part of ChannelPrivModes
228
 	// get voice, since it's not a part of ChannelPrivModes
229
-	if channel.members.HasMode(client, permission) {
229
+	if clientModes.HasMode(permission) {
230
 		return true
230
 		return true
231
 	}
231
 	}
232
 
232
 
233
 	// check regular modes
233
 	// check regular modes
234
 	for _, mode := range modes.ChannelPrivModes {
234
 	for _, mode := range modes.ChannelPrivModes {
235
-		if channel.members.HasMode(client, mode) {
235
+		if clientModes.HasMode(mode) {
236
 			return true
236
 			return true
237
 		}
237
 		}
238
 
238
 
263
 	targetModes := channel.members[target]
263
 	targetModes := channel.members[target]
264
 	result := false
264
 	result := false
265
 	for _, mode := range modes.ChannelPrivModes {
265
 	for _, mode := range modes.ChannelPrivModes {
266
-		if clientModes[mode] {
266
+		if clientModes.HasMode(mode) {
267
 			result = true
267
 			result = true
268
 			// admins cannot kick other admins
268
 			// admins cannot kick other admins
269
-			if mode == modes.ChannelAdmin && targetModes[modes.ChannelAdmin] {
269
+			if mode == modes.ChannelAdmin && targetModes.HasMode(modes.ChannelAdmin) {
270
 				result = false
270
 				result = false
271
 			}
271
 			}
272
 			break
272
 			break
273
-		} else if channel.members[target][mode] {
273
+		} else if targetModes.HasMode(mode) {
274
 			break
274
 			break
275
 		}
275
 		}
276
 	}
276
 	}
331
 		mods += modes.UserLimit.String()
331
 		mods += modes.UserLimit.String()
332
 	}
332
 	}
333
 
333
 
334
+	mods += channel.flags.String()
335
+
334
 	channel.stateMutex.RLock()
336
 	channel.stateMutex.RLock()
335
 	defer channel.stateMutex.RUnlock()
337
 	defer channel.stateMutex.RUnlock()
336
 
338
 
337
-	// flags
338
-	for mode := range channel.flags {
339
-		mods += mode.String()
340
-	}
341
-
342
 	result = []string{mods}
339
 	result = []string{mods}
343
 
340
 
344
 	// args for flags with args: The order must match above to keep
341
 	// args for flags with args: The order must match above to keep
395
 	}
392
 	}
396
 
393
 
397
 	isInvited := channel.lists[modes.InviteMask].Match(client.nickMaskCasefolded)
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
 		rb.Add(nil, client.server.name, ERR_INVITEONLYCHAN, channel.name, fmt.Sprintf(client.t("Cannot join channel (+%s)"), "i"))
396
 		rb.Add(nil, client.server.name, ERR_INVITEONLYCHAN, channel.name, fmt.Sprintf(client.t("Cannot join channel (+%s)"), "i"))
400
 		return
397
 		return
401
 	}
398
 	}
446
 		givenMode = &modes.ChannelOperator
443
 		givenMode = &modes.ChannelOperator
447
 	}
444
 	}
448
 	if givenMode != nil {
445
 	if givenMode != nil {
449
-		channel.members[client][*givenMode] = true
446
+		channel.members[client].SetMode(*givenMode, true)
450
 	}
447
 	}
451
 	channel.stateMutex.Unlock()
448
 	channel.stateMutex.Unlock()
452
 
449
 
515
 
512
 
516
 // SetTopic sets the topic of this channel, if the client is allowed to do so.
513
 // SetTopic sets the topic of this channel, if the client is allowed to do so.
517
 func (channel *Channel) SetTopic(client *Client, topic string, rb *ResponseBuffer) {
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
 		rb.Add(nil, client.server.name, ERR_NOTONCHANNEL, channel.name, client.t("You're not on that channel"))
516
 		rb.Add(nil, client.server.name, ERR_NOTONCHANNEL, channel.name, client.t("You're not on that channel"))
520
 		return
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
 		rb.Add(nil, client.server.name, ERR_CHANOPRIVSNEEDED, channel.name, client.t("You're not a channel operator"))
521
 		rb.Add(nil, client.server.name, ERR_CHANOPRIVSNEEDED, channel.name, client.t("You're not a channel operator"))
525
 		return
522
 		return
526
 	}
523
 	}
552
 	defer channel.stateMutex.RUnlock()
549
 	defer channel.stateMutex.RUnlock()
553
 
550
 
554
 	_, hasClient := channel.members[client]
551
 	_, hasClient := channel.members[client]
555
-	if channel.flags[modes.NoOutside] && !hasClient {
552
+	if channel.flags.HasMode(modes.NoOutside) && !hasClient {
556
 		return false
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
 		return false
556
 		return false
560
 	}
557
 	}
561
-	if channel.flags[modes.RegisteredOnly] && client.Account() == "" {
558
+	if channel.flags.HasMode(modes.RegisteredOnly) && client.Account() == "" {
562
 		return false
559
 		return false
563
 	}
560
 	}
564
 	return true
561
 	return true
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
 	casefoldedName, err := CasefoldName(nick)
683
 	casefoldedName, err := CasefoldName(nick)
693
 	target := channel.server.clients.Get(casefoldedName)
684
 	target := channel.server.clients.Get(casefoldedName)
694
 	if err != nil || target == nil {
685
 	if err != nil || target == nil {
698
 
689
 
699
 	channel.stateMutex.Lock()
690
 	channel.stateMutex.Lock()
700
 	modeset, exists := channel.members[target]
691
 	modeset, exists := channel.members[target]
701
-	var already bool
702
 	if exists {
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
 	channel.stateMutex.Unlock()
701
 	channel.stateMutex.Unlock()
708
 
702
 
709
 	if !exists {
703
 	if !exists {
710
 		rb.Add(nil, client.server.name, ERR_USERNOTINCHANNEL, client.nick, channel.name, client.t("They aren't on that channel"))
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
 // ShowMaskList shows the given list to the client.
709
 // ShowMaskList shows the given list to the client.
790
 }
776
 }
791
 
777
 
792
 func (channel *Channel) Kick(client *Client, target *Client, comment string, rb *ResponseBuffer) {
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
 		rb.Add(nil, client.server.name, ERR_NOTONCHANNEL, channel.name, client.t("You're not on that channel"))
780
 		rb.Add(nil, client.server.name, ERR_NOTONCHANNEL, channel.name, client.t("You're not on that channel"))
795
 		return
781
 		return
796
 	}
782
 	}
823
 
809
 
824
 // Invite invites the given client to the channel, if the inviter can do so.
810
 // Invite invites the given client to the channel, if the inviter can do so.
825
 func (channel *Channel) Invite(invitee *Client, inviter *Client, rb *ResponseBuffer) {
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
 		rb.Add(nil, inviter.server.name, ERR_CHANOPRIVSNEEDED, channel.name, inviter.t("You're not a channel operator"))
813
 		rb.Add(nil, inviter.server.name, ERR_CHANOPRIVSNEEDED, channel.name, inviter.t("You're not a channel operator"))
828
 		return
814
 		return
829
 	}
815
 	}
834
 	}
820
 	}
835
 
821
 
836
 	//TODO(dan): handle this more nicely, keep a list of last X invited channels on invitee rather than explicitly modifying the invite list?
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
 		nmc := invitee.NickCasefolded()
824
 		nmc := invitee.NickCasefolded()
839
 		channel.stateMutex.Lock()
825
 		channel.stateMutex.Lock()
840
 		channel.lists[modes.InviteMask].Add(nmc)
826
 		channel.lists[modes.InviteMask].Add(nmc)
850
 	//TODO(dan): should inviter.server.name here be inviter.nickMaskString ?
836
 	//TODO(dan): should inviter.server.name here be inviter.nickMaskString ?
851
 	rb.Add(nil, inviter.server.name, RPL_INVITING, invitee.nick, channel.name)
837
 	rb.Add(nil, inviter.server.name, RPL_INVITING, invitee.nick, channel.name)
852
 	invitee.Send(nil, inviter.nickMaskString, "INVITE", invitee.nick, channel.name)
838
 	invitee.Send(nil, inviter.nickMaskString, "INVITE", invitee.nick, channel.name)
853
-	if invitee.flags[modes.Away] {
839
+	if invitee.HasMode(modes.Away) {
854
 		rb.Add(nil, inviter.server.name, RPL_AWAY, invitee.nick, invitee.awayMessage)
840
 		rb.Add(nil, inviter.server.name, RPL_AWAY, invitee.nick, invitee.awayMessage)
855
 	}
841
 	}
856
 }
842
 }

+ 2
- 2
irc/chanserv.go 查看文件

199
 	if client == target {
199
 	if client == target {
200
 		givenMode = modes.ChannelFounder
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
 	if change != nil {
203
 	if change != nil {
204
 		//TODO(dan): we should change the name of String and make it return a slice here
204
 		//TODO(dan): we should change the name of String and make it return a slice here
205
 		//TODO(dan): unify this code with code in modes.go
205
 		//TODO(dan): unify this code with code in modes.go
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))
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
 	// give them founder privs
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
 	if change != nil {
264
 	if change != nil {
265
 		//TODO(dan): we should change the name of String and make it return a slice here
265
 		//TODO(dan): we should change the name of String and make it return a slice here
266
 		//TODO(dan): unify this code with code in modes.go
266
 		//TODO(dan): unify this code with code in modes.go

+ 4
- 10
irc/client.go 查看文件

50
 	ctime              time.Time
50
 	ctime              time.Time
51
 	exitedSnomaskSent  bool
51
 	exitedSnomaskSent  bool
52
 	fakelag            *Fakelag
52
 	fakelag            *Fakelag
53
-	flags              map[modes.Mode]bool
53
+	flags              *modes.ModeSet
54
 	hasQuit            bool
54
 	hasQuit            bool
55
 	hops               int
55
 	hops               int
56
 	hostname           string
56
 	hostname           string
98
 		capVersion:     caps.Cap301,
98
 		capVersion:     caps.Cap301,
99
 		channels:       make(ChannelSet),
99
 		channels:       make(ChannelSet),
100
 		ctime:          now,
100
 		ctime:          now,
101
-		flags:          make(map[modes.Mode]bool),
101
+		flags:          modes.NewModeSet(),
102
 		server:         server,
102
 		server:         server,
103
 		socket:         socket,
103
 		socket:         socket,
104
 		nick:           "*", // * is used until actual nick is given
104
 		nick:           "*", // * is used until actual nick is given
109
 
109
 
110
 	client.recomputeMaxlens()
110
 	client.recomputeMaxlens()
111
 	if isTLS {
111
 	if isTLS {
112
-		client.flags[modes.TLS] = true
112
+		client.SetMode(modes.TLS, true)
113
 
113
 
114
 		// error is not useful to us here anyways so we can ignore it
114
 		// error is not useful to us here anyways so we can ignore it
115
 		client.certfp, _ = client.socket.CertFP()
115
 		client.certfp, _ = client.socket.CertFP()
504
 
504
 
505
 // ModeString returns the mode string for this client.
505
 // ModeString returns the mode string for this client.
506
 func (client *Client) ModeString() (str string) {
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
 // Friends refers to clients that share a channel with this client.
510
 // Friends refers to clients that share a channel with this client.

+ 8
- 2
irc/config.go 查看文件

21
 	"github.com/oragono/oragono/irc/custime"
21
 	"github.com/oragono/oragono/irc/custime"
22
 	"github.com/oragono/oragono/irc/languages"
22
 	"github.com/oragono/oragono/irc/languages"
23
 	"github.com/oragono/oragono/irc/logger"
23
 	"github.com/oragono/oragono/irc/logger"
24
+	"github.com/oragono/oragono/irc/modes"
24
 	"github.com/oragono/oragono/irc/passwd"
25
 	"github.com/oragono/oragono/irc/passwd"
25
 	"github.com/oragono/oragono/irc/utils"
26
 	"github.com/oragono/oragono/irc/utils"
26
 	"gopkg.in/yaml.v2"
27
 	"gopkg.in/yaml.v2"
352
 	WhoisLine string
353
 	WhoisLine string
353
 	Vhost     string
354
 	Vhost     string
354
 	Pass      []byte
355
 	Pass      []byte
355
-	Modes     string
356
+	Modes     []modes.ModeChange
356
 }
357
 }
357
 
358
 
358
 // Operators returns a map of operator configs from the given OperClass and config.
359
 // Operators returns a map of operator configs from the given OperClass and config.
379
 		} else {
380
 		} else {
380
 			oper.WhoisLine = class.WhoisLine
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
 		// successful, attach to list of opers
390
 		// successful, attach to list of opers
385
 		operators[name] = oper
391
 		operators[name] = oper

+ 1
- 5
irc/gateways.go 查看文件

81
 
81
 
82
 	// set tls info
82
 	// set tls info
83
 	client.certfp = ""
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
 	return false
86
 	return false
91
 }
87
 }

+ 6
- 24
irc/getters.go 查看文件

188
 }
188
 }
189
 
189
 
190
 func (client *Client) HasMode(mode modes.Mode) bool {
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
 func (client *Client) Channels() (result []*Channel) {
199
 func (client *Client) Channels() (result []*Channel) {
260
 	channel.key = key
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
 func (channel *Channel) Founder() string {
266
 func (channel *Channel) Founder() string {
270
 	channel.stateMutex.RLock()
267
 	channel.stateMutex.RLock()
271
 	defer channel.stateMutex.RUnlock()
268
 	defer channel.stateMutex.RUnlock()
272
 	return channel.registeredFounder
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
 		}
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
 	client.awayMessage = text
409
 	client.awayMessage = text
414
 
410
 
415
 	var op modes.ModeOp
411
 	var op modes.ModeOp
416
-	if client.flags[modes.Away] {
412
+	if isAway {
417
 		op = modes.Add
413
 		op = modes.Add
418
 		rb.Add(nil, server.name, RPL_NOWAWAY, client.nick, client.t("You have been marked as being away"))
414
 		rb.Add(nil, server.name, RPL_NOWAWAY, client.nick, client.t("You have been marked as being away"))
419
 	} else {
415
 	} else {
429
 
425
 
430
 	// dispatch away-notify
426
 	// dispatch away-notify
431
 	for friend := range client.Friends(caps.AwayNotify) {
427
 	for friend := range client.Friends(caps.AwayNotify) {
432
-		if client.flags[modes.Away] {
428
+		if isAway {
433
 			friend.SendFromClient("", client, nil, "AWAY", client.awayMessage)
429
 			friend.SendFromClient("", client, nil, "AWAY", client.awayMessage)
434
 		} else {
430
 		} else {
435
 			friend.SendFromClient("", client, nil, "AWAY")
431
 			friend.SendFromClient("", client, nil, "AWAY")
777
 
773
 
778
 	// handle index
774
 	// handle index
779
 	if argument == "index" {
775
 	if argument == "index" {
780
-		if client.flags[modes.Operator] {
776
+		if client.HasMode(modes.Operator) {
781
 			client.sendHelp("HELP", GetHelpIndex(client.languages, HelpIndexOpers), rb)
777
 			client.sendHelp("HELP", GetHelpIndex(client.languages, HelpIndexOpers), rb)
782
 		} else {
778
 		} else {
783
 			client.sendHelp("HELP", GetHelpIndex(client.languages, HelpIndex), rb)
779
 			client.sendHelp("HELP", GetHelpIndex(client.languages, HelpIndex), rb)
787
 
783
 
788
 	helpHandler, exists := Help[argument]
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
 		if helpHandler.textGenerator != nil {
787
 		if helpHandler.textGenerator != nil {
792
 			client.sendHelp(strings.ToUpper(argument), client.t(helpHandler.textGenerator(client)), rb)
788
 			client.sendHelp(strings.ToUpper(argument), client.t(helpHandler.textGenerator(client)), rb)
793
 		} else {
789
 		} else {
1257
 		}
1253
 		}
1258
 	}
1254
 	}
1259
 
1255
 
1256
+	clientIsOp := client.HasMode(modes.Operator)
1260
 	if len(channels) == 0 {
1257
 	if len(channels) == 0 {
1261
 		for _, channel := range server.channels.Channels() {
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
 				continue
1260
 				continue
1264
 			}
1261
 			}
1265
 			if matcher.Matches(channel) {
1262
 			if matcher.Matches(channel) {
1268
 		}
1265
 		}
1269
 	} else {
1266
 	} else {
1270
 		// limit regular users to only listing one channel
1267
 		// limit regular users to only listing one channel
1271
-		if !client.flags[modes.Operator] {
1268
+		if !clientIsOp {
1272
 			channels = channels[:1]
1269
 			channels = channels[:1]
1273
 		}
1270
 		}
1274
 
1271
 
1275
 		for _, chname := range channels {
1272
 		for _, chname := range channels {
1276
 			casefoldedChname, err := CasefoldChannel(chname)
1273
 			casefoldedChname, err := CasefoldChannel(chname)
1277
 			channel := server.channels.Get(casefoldedChname)
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
 				if len(chname) > 0 {
1276
 				if len(chname) > 0 {
1280
 					rb.Add(nil, server.name, ERR_NOSUCHCHANNEL, client.nick, chname, client.t("No such channel"))
1277
 					rb.Add(nil, server.name, ERR_NOSUCHCHANNEL, client.nick, chname, client.t("No such channel"))
1281
 				}
1278
 				}
1329
 	if 1 < len(msg.Params) {
1326
 	if 1 < len(msg.Params) {
1330
 		// parse out real mode changes
1327
 		// parse out real mode changes
1331
 		params := msg.Params[1:]
1328
 		params := msg.Params[1:]
1332
-		changes, unknown := ParseChannelModeChanges(params...)
1329
+		changes, unknown := modes.ParseChannelModeChanges(params...)
1333
 
1330
 
1334
 		// alert for unknown mode changes
1331
 		// alert for unknown mode changes
1335
 		for char := range unknown {
1332
 		for char := range unknown {
1415
 		}
1412
 		}
1416
 
1413
 
1417
 		// apply mode changes
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
 	if len(applied) > 0 {
1418
 	if len(applied) > 0 {
1422
 		rb.Add(nil, client.nickMaskString, "MODE", targetNick, applied.String())
1419
 		rb.Add(nil, client.nickMaskString, "MODE", targetNick, applied.String())
1423
 	} else if hasPrivs {
1420
 	} else if hasPrivs {
1424
 		rb.Add(nil, target.nickMaskString, RPL_UMODEIS, targetNick, target.ModeString())
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
 			masks := server.snomasks.String(client)
1423
 			masks := server.snomasks.String(client)
1427
 			if 0 < len(masks) {
1424
 			if 0 < len(masks) {
1428
 				rb.Add(nil, target.nickMaskString, RPL_SNOMASKIS, targetNick, masks, client.t("Server notice masks"))
1425
 				rb.Add(nil, target.nickMaskString, RPL_SNOMASKIS, targetNick, masks, client.t("Server notice masks"))
1670
 			msgid := server.generateMessageID()
1667
 			msgid := server.generateMessageID()
1671
 			// restrict messages appropriately when +R is set
1668
 			// restrict messages appropriately when +R is set
1672
 			// intentionally make the sending user think the message went through fine
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
 				user.SendSplitMsgFromClient(msgid, client, clientOnlyTags, "NOTICE", user.nick, splitMsg)
1671
 				user.SendSplitMsgFromClient(msgid, client, clientOnlyTags, "NOTICE", user.nick, splitMsg)
1675
 			}
1672
 			}
1676
 			if client.capabilities.Has(caps.EchoMessage) {
1673
 			if client.capabilities.Has(caps.EchoMessage) {
1731
 		rb.Add(nil, server.name, ERR_PASSWDMISMATCH, client.nick, client.t("Password incorrect"))
1728
 		rb.Add(nil, server.name, ERR_PASSWDMISMATCH, client.nick, client.t("Password incorrect"))
1732
 		return true
1729
 		return true
1733
 	}
1730
 	}
1734
-	if client.flags[modes.Operator] == true {
1731
+	if client.HasMode(modes.Operator) == true {
1735
 		rb.Add(nil, server.name, ERR_UNKNOWNERROR, "OPER", client.t("You're already opered-up!"))
1732
 		rb.Add(nil, server.name, ERR_UNKNOWNERROR, "OPER", client.t("You're already opered-up!"))
1736
 		return false
1733
 		return false
1737
 	}
1734
 	}
1760
 		client.updateNickMask("")
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
 	rb.Add(nil, server.name, RPL_YOUREOPER, client.nick, client.t("You are now an IRC operator"))
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
 	rb.Add(nil, server.name, "MODE", client.nick, applied.String())
1770
 	rb.Add(nil, server.name, "MODE", client.nick, applied.String())
1784
 
1771
 
1785
 	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))
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
 	// client may now be unthrottled by the fakelag system
1774
 	// client may now be unthrottled by the fakelag system
1791
 	client.resetFakelag()
1775
 	client.resetFakelag()
1792
 
1776
 
1793
-	client.flags[modes.Operator] = true
1794
 	return false
1777
 	return false
1795
 }
1778
 }
1796
 
1779
 
1905
 			msgid := server.generateMessageID()
1888
 			msgid := server.generateMessageID()
1906
 			// restrict messages appropriately when +R is set
1889
 			// restrict messages appropriately when +R is set
1907
 			// intentionally make the sending user think the message went through fine
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
 				user.SendSplitMsgFromClient(msgid, client, clientOnlyTags, "PRIVMSG", user.nick, splitMsg)
1892
 				user.SendSplitMsgFromClient(msgid, client, clientOnlyTags, "PRIVMSG", user.nick, splitMsg)
1910
 			}
1893
 			}
1911
 			if client.capabilities.Has(caps.EchoMessage) {
1894
 			if client.capabilities.Has(caps.EchoMessage) {
1912
 				rb.AddSplitMessageFromClient(msgid, client, clientOnlyTags, "PRIVMSG", user.nick, splitMsg)
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
 				//TODO(dan): possibly implement cooldown of away notifications to users
1898
 				//TODO(dan): possibly implement cooldown of away notifications to users
1916
 				rb.Add(nil, server.name, RPL_AWAY, user.nick, user.awayMessage)
1899
 				rb.Add(nil, server.name, RPL_AWAY, user.nick, user.awayMessage)
1917
 			}
1900
 			}
2159
 			if client.capabilities.Has(caps.EchoMessage) {
2142
 			if client.capabilities.Has(caps.EchoMessage) {
2160
 				rb.AddFromClient(msgid, client, clientOnlyTags, "TAGMSG", user.nick)
2143
 				rb.AddFromClient(msgid, client, clientOnlyTags, "TAGMSG", user.nick)
2161
 			}
2144
 			}
2162
-			if user.flags[modes.Away] {
2145
+			if user.HasMode(modes.Away) {
2163
 				//TODO(dan): possibly implement cooldown of away notifications to users
2146
 				//TODO(dan): possibly implement cooldown of away notifications to users
2164
 				rb.Add(nil, server.name, RPL_AWAY, user.nick, user.awayMessage)
2147
 				rb.Add(nil, server.name, RPL_AWAY, user.nick, user.awayMessage)
2165
 			}
2148
 			}
2355
 
2338
 
2356
 		var isOper, isAway string
2339
 		var isOper, isAway string
2357
 
2340
 
2358
-		if target.flags[modes.Operator] {
2341
+		if target.HasMode(modes.Operator) {
2359
 			isOper = "*"
2342
 			isOper = "*"
2360
 		}
2343
 		}
2361
-		if target.flags[modes.Away] {
2344
+		if target.HasMode(modes.Away) {
2362
 			isAway = "-"
2345
 			isAway = "-"
2363
 		} else {
2346
 		} else {
2364
 			isAway = "+"
2347
 			isAway = "+"
2399
 			lkey := strings.ToLower(key)
2382
 			lkey := strings.ToLower(key)
2400
 			if lkey == "tls" || lkey == "secure" {
2383
 			if lkey == "tls" || lkey == "secure" {
2401
 				// only accept "tls" flag if the gateway's connection to us is secure as well
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
 					secure = true
2386
 					secure = true
2404
 				}
2387
 				}
2405
 			}
2388
 			}
2488
 		return false
2471
 		return false
2489
 	}
2472
 	}
2490
 
2473
 
2491
-	if client.flags[modes.Operator] {
2474
+	if client.HasMode(modes.Operator) {
2492
 		masks := strings.Split(masksString, ",")
2475
 		masks := strings.Split(masksString, ",")
2493
 		for _, mask := range masks {
2476
 		for _, mask := range masks {
2494
 			casefoldedMask, err := Casefold(mask)
2477
 			casefoldedMask, err := Casefold(mask)

+ 33
- 111
irc/modes.go 查看文件

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
 	applied := make(modes.ModeChanges, 0)
26
 	applied := make(modes.ModeChanges, 0)
27
 
27
 
28
 	for _, change := range changes {
28
 	for _, change := range changes {
34
 					continue
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
 			case modes.Remove:
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
 		case modes.ServerNotice:
57
 		case modes.ServerNotice:
66
-			if !client.flags[modes.Operator] {
58
+			if !client.HasMode(modes.Operator) {
67
 				continue
59
 				continue
68
 			}
60
 			}
69
 			var masks []sno.Mask
61
 			var masks []sno.Mask
101
 		return DefaultChannelModes
93
 		return DefaultChannelModes
102
 	}
94
 	}
103
 	modeChangeStrings := strings.Split(strings.TrimSpace(*config.Channels.DefaultModes), " ")
95
 	modeChangeStrings := strings.Split(strings.TrimSpace(*config.Channels.DefaultModes), " ")
104
-	modeChanges, _ := ParseChannelModeChanges(modeChangeStrings...)
96
+	modeChanges, _ := modes.ParseChannelModeChanges(modeChangeStrings...)
105
 	defaultChannelModes := make(modes.Modes, 0)
97
 	defaultChannelModes := make(modes.Modes, 0)
106
 	for _, modeChange := range modeChanges {
98
 	for _, modeChange := range modeChanges {
107
 		if modeChange.Op == modes.Add {
99
 		if modeChange.Op == modes.Add {
111
 	return defaultChannelModes
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
 // ApplyChannelModeChanges applies a given set of mode changes.
106
 // ApplyChannelModeChanges applies a given set of mode changes.
192
 func (channel *Channel) ApplyChannelModeChanges(client *Client, isSamode bool, changes modes.ModeChanges, rb *ResponseBuffer) modes.ModeChanges {
107
 func (channel *Channel) ApplyChannelModeChanges(client *Client, isSamode bool, changes modes.ModeChanges, rb *ResponseBuffer) modes.ModeChanges {
193
 	// so we only output one warning for each list type when full
108
 	// so we only output one warning for each list type when full
208
 		}
123
 		}
209
 		switch change.Mode {
124
 		switch change.Mode {
210
 		case modes.ChannelFounder, modes.ChannelAdmin, modes.ChannelOperator, modes.Halfop, modes.Voice:
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
 			if change.Op == modes.List {
127
 			if change.Op == modes.List {
216
 				return true
128
 				return true
217
 			}
129
 			}
218
 			cfarg, _ := CasefoldName(change.Arg)
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
 				// "There is no restriction, however, on anyone `deopping' themselves"
137
 				// "There is no restriction, however, on anyone `deopping' themselves"
221
 				// <https://tools.ietf.org/html/rfc2812#section-3.1.5>
138
 				// <https://tools.ietf.org/html/rfc2812#section-3.1.5>
222
 				return true
139
 				return true
299
 				continue
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
 				applied = append(applied, change)
220
 				applied = append(applied, change)
305
 			}
221
 			}
306
 
222
 
309
 				continue
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
 			if change != nil {
235
 			if change != nil {
314
 				applied = append(applied, *change)
236
 				applied = append(applied, *change)
315
 			}
237
 			}

+ 136
- 13
irc/modes/modes.go 查看文件

7
 
7
 
8
 import (
8
 import (
9
 	"strings"
9
 	"strings"
10
+	"sync"
10
 )
11
 )
11
 
12
 
12
 var (
13
 var (
247
 	return changes, unknown
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
 // ModeSet holds a set of modes.
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
 // String returns the modes in this set.
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
 		return ""
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
 // Prefixes returns a list of prefixes for the given set of channel modes.
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
 	// add prefixes in order from highest to lowest privs
394
 	// add prefixes in order from highest to lowest privs
272
 	for _, mode := range ChannelPrivModes {
395
 	for _, mode := range ChannelPrivModes {
273
-		if set[mode] {
396
+		if set.modes[mode] {
274
 			prefixes += ChannelModePrefixes[mode]
397
 			prefixes += ChannelModePrefixes[mode]
275
 		}
398
 		}
276
 	}
399
 	}
277
-	if set[Voice] {
400
+	if set.modes[Voice] {
278
 		prefixes += ChannelModePrefixes[Voice]
401
 		prefixes += ChannelModePrefixes[Voice]
279
 	}
402
 	}
280
 
403
 

+ 37
- 0
irc/modes/modes_test.go 查看文件

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

+ 8
- 8
irc/server.go 查看文件

637
 	var chstrs []string
637
 	var chstrs []string
638
 	for _, channel := range target.Channels() {
638
 	for _, channel := range target.Channels() {
639
 		// channel is secret and the target can't see it
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
 				continue
642
 				continue
643
 			}
643
 			}
644
 		}
644
 		}
660
 	if target.class != nil {
660
 	if target.class != nil {
661
 		rb.Add(nil, client.server.name, RPL_WHOISOPERATOR, client.nick, target.nick, target.whoisLine)
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
 		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"))
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
 		rb.Add(nil, client.server.name, RPL_WHOISSECURE, client.nick, target.nick, client.t("is using a secure connection"))
667
 		rb.Add(nil, client.server.name, RPL_WHOISSECURE, client.nick, target.nick, client.t("is using a secure connection"))
668
 	}
668
 	}
669
 	if target.LoggedIntoAccount() {
669
 	if target.LoggedIntoAccount() {
670
 		rb.Add(nil, client.server.name, RPL_WHOISACCOUNT, client.nick, target.AccountName(), client.t("is logged in as"))
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
 		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)))
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
 		rb.Add(nil, client.server.name, RPL_WHOISLANGUAGE, params...)
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
 		rb.Add(nil, client.server.name, RPL_WHOISCERTFP, client.nick, target.nick, fmt.Sprintf(client.t("has client certificate fingerprint %s"), target.certfp))
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
 	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"))
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
 
713
 
714
 func whoChannel(client *Client, channel *Channel, friends ClientSet, rb *ResponseBuffer) {
714
 func whoChannel(client *Client, channel *Channel, friends ClientSet, rb *ResponseBuffer) {
715
 	for _, member := range channel.Members() {
715
 	for _, member := range channel.Members() {
716
-		if !client.flags[modes.Invisible] || friends[client] {
716
+		if !client.HasMode(modes.Invisible) || friends[client] {
717
 			client.rplWhoReply(channel, member, rb)
717
 			client.rplWhoReply(channel, member, rb)
718
 		}
718
 		}
719
 	}
719
 	}
1209
 func (target *Client) RplList(channel *Channel, rb *ResponseBuffer) {
1209
 func (target *Client) RplList(channel *Channel, rb *ResponseBuffer) {
1210
 	// get the correct number of channel members
1210
 	// get the correct number of channel members
1211
 	var memberCount int
1211
 	var memberCount int
1212
-	if target.flags[modes.Operator] || channel.hasClient(target) {
1212
+	if target.HasMode(modes.Operator) || channel.hasClient(target) {
1213
 		memberCount = len(channel.Members())
1213
 		memberCount = len(channel.Members())
1214
 	} else {
1214
 	} else {
1215
 		for _, member := range channel.Members() {
1215
 		for _, member := range channel.Members() {

+ 3
- 12
irc/types.go 查看文件

26
 }
26
 }
27
 
27
 
28
 // MemberSet is a set of members with modes.
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
 // Add adds the given client to this set.
31
 // Add adds the given client to this set.
32
 func (members MemberSet) Add(member *Client) {
32
 func (members MemberSet) Add(member *Client) {
33
-	members[member] = make(modes.ModeSet)
33
+	members[member] = modes.NewModeSet()
34
 }
34
 }
35
 
35
 
36
 // Remove removes the given client from this set.
36
 // Remove removes the given client from this set.
44
 	return ok
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
 // AnyHasMode returns true if any of our clients has the given mode.
47
 // AnyHasMode returns true if any of our clients has the given mode.
57
 func (members MemberSet) AnyHasMode(mode modes.Mode) bool {
48
 func (members MemberSet) AnyHasMode(mode modes.Mode) bool {
58
 	for _, modes := range members {
49
 	for _, modes := range members {
59
-		if modes[mode] {
50
+		if modes.HasMode(mode) {
60
 			return true
51
 			return true
61
 		}
52
 		}
62
 	}
53
 	}

正在加载...
取消
保存