|
@@ -178,8 +178,8 @@ func SplitChannelMembershipPrefixes(target string) (prefixes string, name string
|
178
|
178
|
}
|
179
|
179
|
|
180
|
180
|
// GetLowestChannelModePrefix returns the lowest channel prefix mode out of the given prefixes.
|
181
|
|
-func GetLowestChannelModePrefix(prefixes string) *ChannelMode {
|
182
|
|
- var lowest *ChannelMode
|
|
181
|
+func GetLowestChannelModePrefix(prefixes string) *Mode {
|
|
182
|
+ var lowest *Mode
|
183
|
183
|
|
184
|
184
|
if strings.Contains(prefixes, "+") {
|
185
|
185
|
lowest = &Voice
|
|
@@ -208,8 +208,8 @@ func modeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
208
|
208
|
return umodeHandler(server, client, msg)
|
209
|
209
|
}
|
210
|
210
|
|
211
|
|
-// applyModeChanges applies the given changes, and returns the applied changes.
|
212
|
|
-func (client *Client) applyModeChanges(ModeChanges) ModeChanges {
|
|
211
|
+// applyUserModeChanges applies the given changes, and returns the applied changes.
|
|
212
|
+func (client *Client) applyUserModeChanges(changes ModeChanges) ModeChanges {
|
213
|
213
|
applied := make(ModeChanges, 0)
|
214
|
214
|
|
215
|
215
|
for _, change := range changes {
|
|
@@ -289,13 +289,13 @@ func umodeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
289
|
289
|
op = ModeOp(mode)
|
290
|
290
|
continue
|
291
|
291
|
}
|
292
|
|
- changes = append(changes, &ModeChange{
|
|
292
|
+ changes = append(changes, ModeChange{
|
293
|
293
|
mode: Mode(mode),
|
294
|
294
|
op: op,
|
295
|
295
|
})
|
296
|
296
|
}
|
297
|
297
|
|
298
|
|
- applied := target.applyModeChanges(changes)
|
|
298
|
+ applied = target.applyUserModeChanges(changes)
|
299
|
299
|
}
|
300
|
300
|
|
301
|
301
|
if len(applied) > 0 {
|
|
@@ -306,68 +306,45 @@ func umodeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
306
|
306
|
return false
|
307
|
307
|
}
|
308
|
308
|
|
309
|
|
-//////
|
310
|
|
-//////
|
311
|
|
-//////
|
312
|
|
-//////
|
313
|
|
-//////
|
314
|
|
-//////
|
315
|
|
-//////
|
316
|
|
-//////
|
317
|
|
-//////
|
318
|
|
-
|
319
|
|
-// MODE <target> [<modestring> [<mode arguments>...]]
|
320
|
|
-func cmodeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
321
|
|
- channelName, err := CasefoldChannel(msg.Params[0])
|
322
|
|
- channel := server.channels.Get(channelName)
|
323
|
|
-
|
324
|
|
- channel.membersMutex.Lock()
|
325
|
|
- defer channel.membersMutex.Unlock()
|
326
|
|
-
|
327
|
|
- if err != nil || channel == nil {
|
328
|
|
- client.Send(nil, server.name, ERR_NOSUCHCHANNEL, client.nick, msg.Params[0], "No such channel")
|
329
|
|
- return false
|
330
|
|
- }
|
331
|
|
-
|
332
|
|
- // assemble changes
|
333
|
|
- //TODO(dan): split out assembling changes into func that returns changes, err
|
334
|
|
- changes := make(ChannelModeChanges, 0)
|
335
|
|
- applied := make(ChannelModeChanges, 0)
|
|
309
|
+// ParseChannelModeChanges returns the valid changes, and the list of unknown chars.
|
|
310
|
+func ParseChannelModeChanges(params ...string) (ModeChanges, map[rune]bool) {
|
|
311
|
+ changes := make(ModeChanges, 0)
|
|
312
|
+ unknown := make(map[rune]bool)
|
336
|
313
|
|
337
|
|
- // TODO(dan): look at separating these into the type A/B/C/D args and using those lists here
|
338
|
|
- if len(msg.Params) > 1 {
|
339
|
|
- modeArg := msg.Params[1]
|
|
314
|
+ if len(params) > 1 {
|
|
315
|
+ modeArg := params[0]
|
340
|
316
|
op := ModeOp(modeArg[0])
|
341
|
317
|
if (op == Add) || (op == Remove) {
|
342
|
318
|
modeArg = modeArg[1:]
|
343
|
319
|
} else {
|
344
|
|
- client.Send(nil, server.name, ERR_UNKNOWNMODE, client.nick, string(modeArg[0]), "is an unknown mode character to me")
|
345
|
|
- return false
|
|
320
|
+ unknown[rune(modeArg[0])] = true
|
|
321
|
+ return changes, unknown
|
346
|
322
|
}
|
347
|
323
|
|
348
|
|
- skipArgs := 2
|
|
324
|
+ skipArgs := 1
|
|
325
|
+
|
349
|
326
|
for _, mode := range modeArg {
|
350
|
327
|
if mode == '-' || mode == '+' {
|
351
|
328
|
op = ModeOp(mode)
|
352
|
329
|
continue
|
353
|
330
|
}
|
354
|
|
- change := ChannelModeChange{
|
355
|
|
- mode: ChannelMode(mode),
|
|
331
|
+ change := ModeChange{
|
|
332
|
+ mode: Mode(mode),
|
356
|
333
|
op: op,
|
357
|
334
|
}
|
358
|
335
|
|
359
|
336
|
// put arg into modechange if needed
|
360
|
|
- switch ChannelMode(mode) {
|
|
337
|
+ switch Mode(mode) {
|
361
|
338
|
case BanMask, ExceptMask, InviteMask:
|
362
|
|
- if len(msg.Params) > skipArgs {
|
363
|
|
- change.arg = msg.Params[skipArgs]
|
|
339
|
+ if len(params) > skipArgs {
|
|
340
|
+ change.arg = params[skipArgs]
|
364
|
341
|
skipArgs++
|
365
|
342
|
} else {
|
366
|
343
|
change.op = List
|
367
|
344
|
}
|
368
|
345
|
case ChannelFounder, ChannelAdmin, ChannelOperator, Halfop, Voice:
|
369
|
|
- if len(msg.Params) > skipArgs {
|
370
|
|
- change.arg = msg.Params[skipArgs]
|
|
346
|
+ if len(params) > skipArgs {
|
|
347
|
+ change.arg = params[skipArgs]
|
371
|
348
|
skipArgs++
|
372
|
349
|
} else {
|
373
|
350
|
continue
|
|
@@ -375,164 +352,207 @@ func cmodeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
375
|
352
|
case Key, UserLimit:
|
376
|
353
|
// don't require value when removing
|
377
|
354
|
if change.op == Add {
|
378
|
|
- if len(msg.Params) > skipArgs {
|
379
|
|
- change.arg = msg.Params[skipArgs]
|
|
355
|
+ if len(params) > skipArgs {
|
|
356
|
+ change.arg = params[skipArgs]
|
380
|
357
|
skipArgs++
|
381
|
358
|
} else {
|
382
|
359
|
continue
|
383
|
360
|
}
|
384
|
361
|
}
|
|
362
|
+ default:
|
|
363
|
+ unknown[mode] = true
|
385
|
364
|
}
|
386
|
365
|
|
387
|
|
- changes = append(changes, &change)
|
|
366
|
+ changes = append(changes, change)
|
388
|
367
|
}
|
|
368
|
+ }
|
389
|
369
|
|
390
|
|
- // so we only output one warning for each list type when full
|
391
|
|
- listFullWarned := make(map[ChannelMode]bool)
|
|
370
|
+ return changes, unknown
|
|
371
|
+}
|
392
|
372
|
|
393
|
|
- clientIsOp := channel.clientIsAtLeastNoMutex(client, ChannelOperator)
|
394
|
|
- var alreadySentPrivError bool
|
|
373
|
+// ApplyChannelModeChanges applies a given set of mode changes.
|
|
374
|
+func ApplyChannelModeChanges(channel *Channel, client *Client, isSamode bool, changes ModeChanges) ModeChanges {
|
|
375
|
+ // so we only output one warning for each list type when full
|
|
376
|
+ listFullWarned := make(map[Mode]bool)
|
395
|
377
|
|
396
|
|
- for _, change := range changes {
|
397
|
|
- // chan priv modes are checked specially so ignore them
|
398
|
|
- // means regular users can't view ban/except lists... but I'm not worried about that
|
399
|
|
- if msg.Command != "SAMODE" && ChannelModePrefixes[change.mode] == "" && !clientIsOp {
|
400
|
|
- if !alreadySentPrivError {
|
401
|
|
- alreadySentPrivError = true
|
402
|
|
- client.Send(nil, client.server.name, ERR_CHANOPRIVSNEEDED, channel.name, "You're not a channel operator")
|
403
|
|
- }
|
404
|
|
- continue
|
|
378
|
+ clientIsOp := channel.clientIsAtLeastNoMutex(client, ChannelOperator)
|
|
379
|
+ var alreadySentPrivError bool
|
|
380
|
+
|
|
381
|
+ applied := make(ModeChanges, 0)
|
|
382
|
+
|
|
383
|
+ for _, change := range changes {
|
|
384
|
+ // chan priv modes are checked specially so ignore them
|
|
385
|
+ // means regular users can't view ban/except lists... but I'm not worried about that
|
|
386
|
+ if isSamode && ChannelModePrefixes[change.mode] == "" && !clientIsOp {
|
|
387
|
+ if !alreadySentPrivError {
|
|
388
|
+ alreadySentPrivError = true
|
|
389
|
+ client.Send(nil, client.server.name, ERR_CHANOPRIVSNEEDED, channel.name, "You're not a channel operator")
|
405
|
390
|
}
|
|
391
|
+ continue
|
|
392
|
+ }
|
406
|
393
|
|
407
|
|
- switch change.mode {
|
408
|
|
- case BanMask, ExceptMask, InviteMask:
|
409
|
|
- mask := change.arg
|
410
|
|
- list := channel.lists[change.mode]
|
411
|
|
- if list == nil {
|
412
|
|
- // This should never happen, but better safe than panicky.
|
413
|
|
- client.Send(nil, server.name, ERR_UNKNOWNERROR, client.nick, "MODE", "Could not complete MODE command")
|
414
|
|
- return false
|
415
|
|
- }
|
|
394
|
+ switch change.mode {
|
|
395
|
+ case BanMask, ExceptMask, InviteMask:
|
|
396
|
+ mask := change.arg
|
|
397
|
+ list := channel.lists[change.mode]
|
|
398
|
+ if list == nil {
|
|
399
|
+ // This should never happen, but better safe than panicky.
|
|
400
|
+ client.Send(nil, client.server.name, ERR_UNKNOWNERROR, client.nick, "MODE", "Could not complete MODE command")
|
|
401
|
+ return changes
|
|
402
|
+ }
|
416
|
403
|
|
417
|
|
- if (change.op == List) || (mask == "") {
|
418
|
|
- channel.ShowMaskList(client, change.mode)
|
419
|
|
- continue
|
420
|
|
- }
|
|
404
|
+ if (change.op == List) || (mask == "") {
|
|
405
|
+ channel.ShowMaskList(client, change.mode)
|
|
406
|
+ continue
|
|
407
|
+ }
|
|
408
|
+
|
|
409
|
+ // confirm mask looks valid
|
|
410
|
+ mask, err := Casefold(mask)
|
|
411
|
+ if err != nil {
|
|
412
|
+ continue
|
|
413
|
+ }
|
421
|
414
|
|
422
|
|
- // confirm mask looks valid
|
423
|
|
- mask, err = Casefold(mask)
|
424
|
|
- if err != nil {
|
|
415
|
+ switch change.op {
|
|
416
|
+ case Add:
|
|
417
|
+ if len(list.masks) >= client.server.limits.ChanListModes {
|
|
418
|
+ if !listFullWarned[change.mode] {
|
|
419
|
+ client.Send(nil, client.server.name, ERR_BANLISTFULL, client.nick, channel.name, change.mode.String(), "Channel list is full")
|
|
420
|
+ listFullWarned[change.mode] = true
|
|
421
|
+ }
|
425
|
422
|
continue
|
426
|
423
|
}
|
427
|
424
|
|
428
|
|
- switch change.op {
|
429
|
|
- case Add:
|
430
|
|
- if len(list.masks) >= server.limits.ChanListModes {
|
431
|
|
- if !listFullWarned[change.mode] {
|
432
|
|
- client.Send(nil, server.name, ERR_BANLISTFULL, client.nick, channel.name, change.mode.String(), "Channel list is full")
|
433
|
|
- listFullWarned[change.mode] = true
|
434
|
|
- }
|
435
|
|
- continue
|
436
|
|
- }
|
|
425
|
+ list.Add(mask)
|
|
426
|
+ applied = append(applied, change)
|
437
|
427
|
|
438
|
|
- list.Add(mask)
|
439
|
|
- applied = append(applied, change)
|
|
428
|
+ case Remove:
|
|
429
|
+ list.Remove(mask)
|
|
430
|
+ applied = append(applied, change)
|
|
431
|
+ }
|
440
|
432
|
|
441
|
|
- case Remove:
|
442
|
|
- list.Remove(mask)
|
|
433
|
+ case UserLimit:
|
|
434
|
+ switch change.op {
|
|
435
|
+ case Add:
|
|
436
|
+ val, err := strconv.ParseUint(change.arg, 10, 64)
|
|
437
|
+ if err == nil {
|
|
438
|
+ channel.userLimit = val
|
443
|
439
|
applied = append(applied, change)
|
444
|
440
|
}
|
445
|
441
|
|
446
|
|
- case UserLimit:
|
447
|
|
- switch change.op {
|
448
|
|
- case Add:
|
449
|
|
- val, err := strconv.ParseUint(change.arg, 10, 64)
|
450
|
|
- if err == nil {
|
451
|
|
- channel.userLimit = val
|
452
|
|
- applied = append(applied, change)
|
453
|
|
- }
|
|
442
|
+ case Remove:
|
|
443
|
+ channel.userLimit = 0
|
|
444
|
+ applied = append(applied, change)
|
|
445
|
+ }
|
454
|
446
|
|
455
|
|
- case Remove:
|
456
|
|
- channel.userLimit = 0
|
457
|
|
- applied = append(applied, change)
|
458
|
|
- }
|
|
447
|
+ case Key:
|
|
448
|
+ switch change.op {
|
|
449
|
+ case Add:
|
|
450
|
+ channel.key = change.arg
|
459
|
451
|
|
460
|
|
- case Key:
|
461
|
|
- switch change.op {
|
462
|
|
- case Add:
|
463
|
|
- channel.key = change.arg
|
|
452
|
+ case Remove:
|
|
453
|
+ channel.key = ""
|
|
454
|
+ }
|
|
455
|
+ applied = append(applied, change)
|
464
|
456
|
|
465
|
|
- case Remove:
|
466
|
|
- channel.key = ""
|
|
457
|
+ case InviteOnly, Moderated, NoOutside, OpOnlyTopic, Secret, ChanRoleplaying:
|
|
458
|
+ switch change.op {
|
|
459
|
+ case Add:
|
|
460
|
+ if channel.flags[change.mode] {
|
|
461
|
+ continue
|
467
|
462
|
}
|
|
463
|
+ channel.flags[change.mode] = true
|
468
|
464
|
applied = append(applied, change)
|
469
|
465
|
|
470
|
|
- case InviteOnly, Moderated, NoOutside, OpOnlyTopic, Secret, ChanRoleplaying:
|
471
|
|
- switch change.op {
|
472
|
|
- case Add:
|
473
|
|
- if channel.flags[change.mode] {
|
474
|
|
- continue
|
475
|
|
- }
|
476
|
|
- channel.flags[change.mode] = true
|
477
|
|
- applied = append(applied, change)
|
478
|
|
-
|
479
|
|
- case Remove:
|
480
|
|
- if !channel.flags[change.mode] {
|
481
|
|
- continue
|
482
|
|
- }
|
483
|
|
- delete(channel.flags, change.mode)
|
484
|
|
- applied = append(applied, change)
|
|
466
|
+ case Remove:
|
|
467
|
+ if !channel.flags[change.mode] {
|
|
468
|
+ continue
|
485
|
469
|
}
|
|
470
|
+ delete(channel.flags, change.mode)
|
|
471
|
+ applied = append(applied, change)
|
|
472
|
+ }
|
486
|
473
|
|
487
|
|
- case ChannelFounder, ChannelAdmin, ChannelOperator, Halfop, Voice:
|
488
|
|
- // make sure client has privs to edit the given prefix
|
489
|
|
- var hasPrivs bool
|
|
474
|
+ case ChannelFounder, ChannelAdmin, ChannelOperator, Halfop, Voice:
|
|
475
|
+ // make sure client has privs to edit the given prefix
|
|
476
|
+ hasPrivs := isSamode
|
490
|
477
|
|
491
|
|
- if msg.Command == "SAMODE" {
|
492
|
|
- hasPrivs = true
|
493
|
|
- } else {
|
494
|
|
- for _, mode := range ChannelPrivModes {
|
495
|
|
- if channel.members[client][mode] {
|
496
|
|
- hasPrivs = true
|
497
|
|
-
|
498
|
|
- // Admins can't give other people Admin or remove it from others,
|
499
|
|
- // standard for that channel mode, we worry about this later
|
500
|
|
- if mode == ChannelAdmin && change.mode == ChannelAdmin {
|
501
|
|
- hasPrivs = false
|
502
|
|
- }
|
503
|
|
-
|
504
|
|
- break
|
505
|
|
- } else if mode == change.mode {
|
506
|
|
- break
|
|
478
|
+ if !hasPrivs {
|
|
479
|
+ for _, mode := range ChannelPrivModes {
|
|
480
|
+ if channel.members[client][mode] {
|
|
481
|
+ hasPrivs = true
|
|
482
|
+
|
|
483
|
+ // Admins can't give other people Admin or remove it from others,
|
|
484
|
+ // standard for that channel mode, we worry about this later
|
|
485
|
+ if mode == ChannelAdmin && change.mode == ChannelAdmin {
|
|
486
|
+ hasPrivs = false
|
507
|
487
|
}
|
|
488
|
+
|
|
489
|
+ break
|
|
490
|
+ } else if mode == change.mode {
|
|
491
|
+ break
|
508
|
492
|
}
|
509
|
493
|
}
|
|
494
|
+ }
|
510
|
495
|
|
511
|
|
- casefoldedName, err := CasefoldName(change.arg)
|
512
|
|
- if err != nil {
|
513
|
|
- continue
|
514
|
|
- }
|
|
496
|
+ casefoldedName, err := CasefoldName(change.arg)
|
|
497
|
+ if err != nil {
|
|
498
|
+ continue
|
|
499
|
+ }
|
515
|
500
|
|
516
|
|
- if !hasPrivs {
|
517
|
|
- if change.op == Remove && casefoldedName == client.nickCasefolded {
|
518
|
|
- // success!
|
519
|
|
- } else {
|
520
|
|
- if !alreadySentPrivError {
|
521
|
|
- alreadySentPrivError = true
|
522
|
|
- client.Send(nil, client.server.name, ERR_CHANOPRIVSNEEDED, channel.name, "You're not a channel operator")
|
523
|
|
- }
|
524
|
|
- continue
|
|
501
|
+ if !hasPrivs {
|
|
502
|
+ if change.op == Remove && casefoldedName == client.nickCasefolded {
|
|
503
|
+ // success!
|
|
504
|
+ } else {
|
|
505
|
+ if !alreadySentPrivError {
|
|
506
|
+ alreadySentPrivError = true
|
|
507
|
+ client.Send(nil, client.server.name, ERR_CHANOPRIVSNEEDED, channel.name, "You're not a channel operator")
|
525
|
508
|
}
|
|
509
|
+ continue
|
526
|
510
|
}
|
|
511
|
+ }
|
527
|
512
|
|
528
|
|
- change := channel.applyModeMemberNoMutex(client, change.mode, change.op, change.arg)
|
529
|
|
- if change != nil {
|
530
|
|
- applied = append(applied, change)
|
531
|
|
- }
|
|
513
|
+ change := channel.applyModeMemberNoMutex(client, change.mode, change.op, change.arg)
|
|
514
|
+ if change != nil {
|
|
515
|
+ applied = append(applied, *change)
|
532
|
516
|
}
|
533
|
517
|
}
|
534
|
518
|
}
|
535
|
519
|
|
|
520
|
+ return applied
|
|
521
|
+}
|
|
522
|
+
|
|
523
|
+// MODE <target> [<modestring> [<mode arguments>...]]
|
|
524
|
+func cmodeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
|
525
|
+ channelName, err := CasefoldChannel(msg.Params[0])
|
|
526
|
+ channel := server.channels.Get(channelName)
|
|
527
|
+
|
|
528
|
+ channel.membersMutex.Lock()
|
|
529
|
+ defer channel.membersMutex.Unlock()
|
|
530
|
+
|
|
531
|
+ if err != nil || channel == nil {
|
|
532
|
+ client.Send(nil, server.name, ERR_NOSUCHCHANNEL, client.nick, msg.Params[0], "No such channel")
|
|
533
|
+ return false
|
|
534
|
+ }
|
|
535
|
+
|
|
536
|
+ // applied mode changes
|
|
537
|
+ applied := make(ModeChanges, 0)
|
|
538
|
+
|
|
539
|
+ if 1 < len(msg.Params) {
|
|
540
|
+ // parse out real mode changes
|
|
541
|
+ params := msg.Params[1:]
|
|
542
|
+ changes, unknown := ParseChannelModeChanges(params...)
|
|
543
|
+
|
|
544
|
+ // alert for unknown mode changes
|
|
545
|
+ for char := range unknown {
|
|
546
|
+ client.Send(nil, server.name, ERR_UNKNOWNMODE, client.nick, string(char), "is an unknown mode character to me")
|
|
547
|
+ }
|
|
548
|
+ if len(unknown) == 1 && len(changes) == 0 {
|
|
549
|
+ return false
|
|
550
|
+ }
|
|
551
|
+
|
|
552
|
+ // apply mode changes
|
|
553
|
+ applied = ApplyChannelModeChanges(channel, client, msg.Command == "SAMODE", changes)
|
|
554
|
+ }
|
|
555
|
+
|
536
|
556
|
if len(applied) > 0 {
|
537
|
557
|
//TODO(dan): we should change the name of String and make it return a slice here
|
538
|
558
|
args := append([]string{channel.name}, strings.Split(applied.String(), " ")...)
|