1// Copyright (c) 2012-2014 Jeremy Latt 2// Copyright (c) 2014-2015 Edmund Huber 3// Copyright (c) 2016-2017 Daniel Oaks <daniel@danieloaks.net> 4// released under the MIT license 5 6package irc 7 8import ( 9 "fmt" 10 "strconv" 11 "strings" 12 "time" 13 14 "sync" 15 16 "github.com/ergochat/irc-go/ircutils" 17 18 "github.com/ergochat/ergo/irc/caps" 19 "github.com/ergochat/ergo/irc/history" 20 "github.com/ergochat/ergo/irc/modes" 21 "github.com/ergochat/ergo/irc/utils" 22) 23 24type ChannelSettings struct { 25 History HistoryStatus 26 QueryCutoff HistoryCutoff 27} 28 29// Channel represents a channel that clients can join. 30type Channel struct { 31 flags modes.ModeSet 32 lists map[modes.Mode]*UserMaskSet 33 key string 34 forward string 35 members MemberSet 36 membersCache []*Client // allow iteration over channel members without holding the lock 37 name string 38 nameCasefolded string 39 server *Server 40 createdTime time.Time 41 registeredFounder string 42 registeredTime time.Time 43 transferPendingTo string 44 topic string 45 topicSetBy string 46 topicSetTime time.Time 47 userLimit int 48 accountToUMode map[string]modes.Mode 49 history history.Buffer 50 stateMutex sync.RWMutex // tier 1 51 writerSemaphore utils.Semaphore // tier 1.5 52 joinPartMutex sync.Mutex // tier 3 53 ensureLoaded utils.Once // manages loading stored registration info from the database 54 dirtyBits uint 55 settings ChannelSettings 56} 57 58// NewChannel creates a new channel from a `Server` and a `name` 59// string, which must be unique on the server. 60func NewChannel(s *Server, name, casefoldedName string, registered bool) *Channel { 61 config := s.Config() 62 63 channel := &Channel{ 64 createdTime: time.Now().UTC(), // may be overwritten by applyRegInfo 65 members: make(MemberSet), 66 name: name, 67 nameCasefolded: casefoldedName, 68 server: s, 69 writerSemaphore: utils.NewSemaphore(1), 70 } 71 72 channel.initializeLists() 73 channel.history.Initialize(0, 0) 74 75 if !registered { 76 channel.resizeHistory(config) 77 for _, mode := range config.Channels.defaultModes { 78 channel.flags.SetMode(mode, true) 79 } 80 // no loading to do, so "mark" the load operation as "done": 81 channel.ensureLoaded.Do(func() {}) 82 } // else: modes will be loaded before first join 83 84 return channel 85} 86 87func (channel *Channel) initializeLists() { 88 channel.lists = map[modes.Mode]*UserMaskSet{ 89 modes.BanMask: NewUserMaskSet(), 90 modes.ExceptMask: NewUserMaskSet(), 91 modes.InviteMask: NewUserMaskSet(), 92 } 93 channel.accountToUMode = make(map[string]modes.Mode) 94} 95 96// EnsureLoaded blocks until the channel's registration info has been loaded 97// from the database. 98func (channel *Channel) EnsureLoaded() { 99 channel.ensureLoaded.Do(func() { 100 nmc := channel.NameCasefolded() 101 info, err := channel.server.channelRegistry.LoadChannel(nmc) 102 if err == nil { 103 channel.applyRegInfo(info) 104 } else { 105 channel.server.logger.Error("internal", "couldn't load channel", nmc, err.Error()) 106 } 107 }) 108} 109 110func (channel *Channel) IsLoaded() bool { 111 return channel.ensureLoaded.Done() 112} 113 114func (channel *Channel) resizeHistory(config *Config) { 115 status, _, _ := channel.historyStatus(config) 116 if status == HistoryEphemeral { 117 channel.history.Resize(config.History.ChannelLength, time.Duration(config.History.AutoresizeWindow)) 118 } else { 119 channel.history.Resize(0, 0) 120 } 121} 122 123// read in channel state that was persisted in the DB 124func (channel *Channel) applyRegInfo(chanReg RegisteredChannel) { 125 defer channel.resizeHistory(channel.server.Config()) 126 127 channel.stateMutex.Lock() 128 defer channel.stateMutex.Unlock() 129 130 channel.registeredFounder = chanReg.Founder 131 channel.registeredTime = chanReg.RegisteredAt 132 channel.topic = chanReg.Topic 133 channel.topicSetBy = chanReg.TopicSetBy 134 channel.topicSetTime = chanReg.TopicSetTime 135 channel.name = chanReg.Name 136 channel.createdTime = chanReg.RegisteredAt 137 channel.key = chanReg.Key 138 channel.userLimit = chanReg.UserLimit 139 channel.settings = chanReg.Settings 140 channel.forward = chanReg.Forward 141 142 for _, mode := range chanReg.Modes { 143 channel.flags.SetMode(mode, true) 144 } 145 for account, mode := range chanReg.AccountToUMode { 146 channel.accountToUMode[account] = mode 147 } 148 channel.lists[modes.BanMask].SetMasks(chanReg.Bans) 149 channel.lists[modes.InviteMask].SetMasks(chanReg.Invites) 150 channel.lists[modes.ExceptMask].SetMasks(chanReg.Excepts) 151} 152 153// obtain a consistent snapshot of the channel state that can be persisted to the DB 154func (channel *Channel) ExportRegistration(includeFlags uint) (info RegisteredChannel) { 155 channel.stateMutex.RLock() 156 defer channel.stateMutex.RUnlock() 157 158 info.Name = channel.name 159 info.NameCasefolded = channel.nameCasefolded 160 info.Founder = channel.registeredFounder 161 info.RegisteredAt = channel.registeredTime 162 163 if includeFlags&IncludeTopic != 0 { 164 info.Topic = channel.topic 165 info.TopicSetBy = channel.topicSetBy 166 info.TopicSetTime = channel.topicSetTime 167 } 168 169 if includeFlags&IncludeModes != 0 { 170 info.Key = channel.key 171 info.Forward = channel.forward 172 info.Modes = channel.flags.AllModes() 173 info.UserLimit = channel.userLimit 174 } 175 176 if includeFlags&IncludeLists != 0 { 177 info.Bans = channel.lists[modes.BanMask].Masks() 178 info.Invites = channel.lists[modes.InviteMask].Masks() 179 info.Excepts = channel.lists[modes.ExceptMask].Masks() 180 info.AccountToUMode = make(map[string]modes.Mode) 181 for account, mode := range channel.accountToUMode { 182 info.AccountToUMode[account] = mode 183 } 184 } 185 186 if includeFlags&IncludeSettings != 0 { 187 info.Settings = channel.settings 188 } 189 190 return 191} 192 193// begin: asynchronous database writeback implementation, modeled on irc/socket.go 194 195// MarkDirty marks part (or all) of a channel's data as needing to be written back 196// to the database, then starts a writer goroutine if necessary. 197// This is the equivalent of Socket.Write(). 198func (channel *Channel) MarkDirty(dirtyBits uint) { 199 channel.stateMutex.Lock() 200 isRegistered := channel.registeredFounder != "" 201 channel.dirtyBits = channel.dirtyBits | dirtyBits 202 channel.stateMutex.Unlock() 203 if !isRegistered { 204 return 205 } 206 207 channel.wakeWriter() 208} 209 210// IsClean returns whether a channel can be safely removed from the server. 211// To avoid the obvious TOCTOU race condition, it must be called while holding 212// ChannelManager's lock (that way, no one can join and make the channel dirty again 213// between this method exiting and the actual deletion). 214func (channel *Channel) IsClean() bool { 215 if !channel.writerSemaphore.TryAcquire() { 216 // a database write (which may fail) is in progress, the channel cannot be cleaned up 217 return false 218 } 219 defer channel.writerSemaphore.Release() 220 221 channel.stateMutex.RLock() 222 defer channel.stateMutex.RUnlock() 223 if len(channel.members) != 0 { 224 return false 225 } 226 // see #1507 and #704 among others; registered channels should never be removed 227 return channel.registeredFounder == "" 228} 229 230func (channel *Channel) wakeWriter() { 231 if channel.writerSemaphore.TryAcquire() { 232 go channel.writeLoop() 233 } 234} 235 236// equivalent of Socket.send() 237func (channel *Channel) writeLoop() { 238 for { 239 // TODO(#357) check the error value of this and implement timed backoff 240 channel.performWrite(0) 241 channel.writerSemaphore.Release() 242 243 channel.stateMutex.RLock() 244 isDirty := channel.dirtyBits != 0 245 isEmpty := len(channel.members) == 0 246 channel.stateMutex.RUnlock() 247 248 if !isDirty { 249 if isEmpty { 250 channel.server.channels.Cleanup(channel) 251 } 252 return // nothing to do 253 } // else: isDirty, so we need to write again 254 255 if !channel.writerSemaphore.TryAcquire() { 256 return 257 } 258 } 259} 260 261// Store writes part (or all) of the channel's data back to the database, 262// blocking until the write is complete. This is the equivalent of 263// Socket.BlockingWrite. 264func (channel *Channel) Store(dirtyBits uint) (err error) { 265 defer func() { 266 channel.stateMutex.Lock() 267 isDirty := channel.dirtyBits != 0 268 isEmpty := len(channel.members) == 0 269 channel.stateMutex.Unlock() 270 271 if isDirty { 272 channel.wakeWriter() 273 } else if isEmpty { 274 channel.server.channels.Cleanup(channel) 275 } 276 }() 277 278 channel.writerSemaphore.Acquire() 279 defer channel.writerSemaphore.Release() 280 return channel.performWrite(dirtyBits) 281} 282 283// do an individual write; equivalent of Socket.send() 284func (channel *Channel) performWrite(additionalDirtyBits uint) (err error) { 285 channel.stateMutex.Lock() 286 dirtyBits := channel.dirtyBits | additionalDirtyBits 287 channel.dirtyBits = 0 288 isRegistered := channel.registeredFounder != "" 289 channel.stateMutex.Unlock() 290 291 if !isRegistered || dirtyBits == 0 { 292 return 293 } 294 295 info := channel.ExportRegistration(dirtyBits) 296 err = channel.server.channelRegistry.StoreChannel(info, dirtyBits) 297 if err != nil { 298 channel.stateMutex.Lock() 299 channel.dirtyBits = channel.dirtyBits | dirtyBits 300 channel.stateMutex.Unlock() 301 } 302 return 303} 304 305// SetRegistered registers the channel, returning an error if it was already registered. 306func (channel *Channel) SetRegistered(founder string) error { 307 channel.stateMutex.Lock() 308 defer channel.stateMutex.Unlock() 309 310 if channel.registeredFounder != "" { 311 return errChannelAlreadyRegistered 312 } 313 channel.registeredFounder = founder 314 channel.registeredTime = time.Now().UTC() 315 channel.accountToUMode[founder] = modes.ChannelFounder 316 return nil 317} 318 319// SetUnregistered deletes the channel's registration information. 320func (channel *Channel) SetUnregistered(expectedFounder string) { 321 channel.stateMutex.Lock() 322 defer channel.stateMutex.Unlock() 323 324 if channel.registeredFounder != expectedFounder { 325 return 326 } 327 channel.registeredFounder = "" 328 var zeroTime time.Time 329 channel.registeredTime = zeroTime 330 channel.accountToUMode = make(map[string]modes.Mode) 331} 332 333// implements `CHANSERV CLEAR #chan ACCESS` (resets bans, invites, excepts, and amodes) 334func (channel *Channel) resetAccess() { 335 defer channel.MarkDirty(IncludeLists) 336 337 channel.stateMutex.Lock() 338 defer channel.stateMutex.Unlock() 339 channel.initializeLists() 340 if channel.registeredFounder != "" { 341 channel.accountToUMode[channel.registeredFounder] = modes.ChannelFounder 342 } 343} 344 345// IsRegistered returns whether the channel is registered. 346func (channel *Channel) IsRegistered() bool { 347 channel.stateMutex.RLock() 348 defer channel.stateMutex.RUnlock() 349 return channel.registeredFounder != "" 350} 351 352type channelTransferStatus uint 353 354const ( 355 channelTransferComplete channelTransferStatus = iota 356 channelTransferPending 357 channelTransferCancelled 358 channelTransferFailed 359) 360 361// Transfer transfers ownership of a registered channel to a different account 362func (channel *Channel) Transfer(client *Client, target string, hasPrivs bool) (status channelTransferStatus, err error) { 363 status = channelTransferFailed 364 defer func() { 365 if status == channelTransferComplete && err == nil { 366 channel.Store(IncludeAllAttrs) 367 } 368 }() 369 370 cftarget, err := CasefoldName(target) 371 if err != nil { 372 err = errAccountDoesNotExist 373 return 374 } 375 channel.stateMutex.Lock() 376 defer channel.stateMutex.Unlock() 377 if channel.registeredFounder == "" { 378 err = errChannelNotOwnedByAccount 379 return 380 } 381 if hasPrivs { 382 channel.transferOwnership(cftarget) 383 return channelTransferComplete, nil 384 } else { 385 if channel.registeredFounder == cftarget { 386 // transferring back to yourself cancels a pending transfer 387 channel.transferPendingTo = "" 388 return channelTransferCancelled, nil 389 } else { 390 channel.transferPendingTo = cftarget 391 return channelTransferPending, nil 392 } 393 } 394} 395 396func (channel *Channel) transferOwnership(newOwner string) { 397 delete(channel.accountToUMode, channel.registeredFounder) 398 channel.registeredFounder = newOwner 399 channel.accountToUMode[channel.registeredFounder] = modes.ChannelFounder 400 channel.transferPendingTo = "" 401} 402 403// AcceptTransfer implements `CS TRANSFER #chan ACCEPT` 404func (channel *Channel) AcceptTransfer(client *Client) (err error) { 405 defer func() { 406 if err == nil { 407 channel.Store(IncludeAllAttrs) 408 } 409 }() 410 411 account := client.Account() 412 if account == "" { 413 return errAccountNotLoggedIn 414 } 415 channel.stateMutex.Lock() 416 defer channel.stateMutex.Unlock() 417 if account != channel.transferPendingTo { 418 return errChannelTransferNotOffered 419 } 420 channel.transferOwnership(account) 421 return nil 422} 423 424func (channel *Channel) regenerateMembersCache() { 425 channel.stateMutex.RLock() 426 result := make([]*Client, len(channel.members)) 427 i := 0 428 for client := range channel.members { 429 result[i] = client 430 i++ 431 } 432 channel.stateMutex.RUnlock() 433 434 channel.stateMutex.Lock() 435 channel.membersCache = result 436 channel.stateMutex.Unlock() 437} 438 439// Names sends the list of users joined to the channel to the given client. 440func (channel *Channel) Names(client *Client, rb *ResponseBuffer) { 441 channel.stateMutex.RLock() 442 clientData, isJoined := channel.members[client] 443 channel.stateMutex.RUnlock() 444 isOper := client.HasRoleCapabs("sajoin") 445 respectAuditorium := channel.flags.HasMode(modes.Auditorium) && !isOper && 446 (!isJoined || clientData.modes.HighestChannelUserMode() == modes.Mode(0)) 447 isMultiPrefix := rb.session.capabilities.Has(caps.MultiPrefix) 448 isUserhostInNames := rb.session.capabilities.Has(caps.UserhostInNames) 449 450 maxNamLen := 480 - len(client.server.name) - len(client.Nick()) 451 var namesLines []string 452 var buffer strings.Builder 453 if isJoined || !channel.flags.HasMode(modes.Secret) || isOper { 454 for _, target := range channel.Members() { 455 var nick string 456 if isUserhostInNames { 457 nick = target.NickMaskString() 458 } else { 459 nick = target.Nick() 460 } 461 channel.stateMutex.RLock() 462 memberData, _ := channel.members[target] 463 channel.stateMutex.RUnlock() 464 modeSet := memberData.modes 465 if modeSet == nil { 466 continue 467 } 468 if !isJoined && target.HasMode(modes.Invisible) && !isOper { 469 continue 470 } 471 if respectAuditorium && modeSet.HighestChannelUserMode() == modes.Mode(0) { 472 continue 473 } 474 prefix := modeSet.Prefixes(isMultiPrefix) 475 if buffer.Len()+len(nick)+len(prefix)+1 > maxNamLen { 476 namesLines = append(namesLines, buffer.String()) 477 buffer.Reset() 478 } 479 if buffer.Len() > 0 { 480 buffer.WriteString(" ") 481 } 482 buffer.WriteString(prefix) 483 buffer.WriteString(nick) 484 } 485 if buffer.Len() > 0 { 486 namesLines = append(namesLines, buffer.String()) 487 } 488 } 489 490 for _, line := range namesLines { 491 if buffer.Len() > 0 { 492 rb.Add(nil, client.server.name, RPL_NAMREPLY, client.nick, "=", channel.name, line) 493 } 494 } 495 rb.Add(nil, client.server.name, RPL_ENDOFNAMES, client.nick, channel.name, client.t("End of NAMES list")) 496} 497 498// does `clientMode` give you privileges to grant/remove `targetMode` to/from people, 499// or to kick them? 500func channelUserModeHasPrivsOver(clientMode modes.Mode, targetMode modes.Mode) bool { 501 switch clientMode { 502 case modes.ChannelFounder: 503 return true 504 case modes.ChannelAdmin, modes.ChannelOperator: 505 // admins cannot kick other admins, operators *can* kick other operators 506 return targetMode != modes.ChannelFounder && targetMode != modes.ChannelAdmin 507 case modes.Halfop: 508 // halfops cannot kick other halfops 509 return targetMode == modes.Voice || targetMode == modes.Mode(0) 510 default: 511 // voice and unprivileged cannot kick anyone 512 return false 513 } 514} 515 516// ClientIsAtLeast returns whether the client has at least the given channel privilege. 517func (channel *Channel) ClientIsAtLeast(client *Client, permission modes.Mode) bool { 518 channel.stateMutex.RLock() 519 memberData := channel.members[client] 520 founder := channel.registeredFounder 521 channel.stateMutex.RUnlock() 522 523 if founder != "" && founder == client.Account() { 524 return true 525 } 526 527 for _, mode := range modes.ChannelUserModes { 528 if memberData.modes.HasMode(mode) { 529 return true 530 } 531 if mode == permission { 532 break 533 } 534 } 535 return false 536} 537 538func (channel *Channel) ClientPrefixes(client *Client, isMultiPrefix bool) string { 539 channel.stateMutex.RLock() 540 defer channel.stateMutex.RUnlock() 541 memberData, present := channel.members[client] 542 if !present { 543 return "" 544 } else { 545 return memberData.modes.Prefixes(isMultiPrefix) 546 } 547} 548 549func (channel *Channel) ClientStatus(client *Client) (present bool, joinTimeSecs int64, cModes modes.Modes) { 550 channel.stateMutex.RLock() 551 defer channel.stateMutex.RUnlock() 552 memberData, present := channel.members[client] 553 return present, time.Unix(0, memberData.joinTime).Unix(), memberData.modes.AllModes() 554} 555 556// helper for persisting channel-user modes for always-on clients; 557// return the channel name and all channel-user modes for a client 558func (channel *Channel) alwaysOnStatus(client *Client) (chname string, status alwaysOnChannelStatus) { 559 channel.stateMutex.RLock() 560 defer channel.stateMutex.RUnlock() 561 chname = channel.name 562 data := channel.members[client] 563 status.Modes = data.modes.String() 564 status.JoinTime = data.joinTime 565 return 566} 567 568// overwrite any existing channel-user modes with the stored ones 569func (channel *Channel) setMemberStatus(client *Client, status alwaysOnChannelStatus) { 570 newModes := modes.NewModeSet() 571 for _, mode := range status.Modes { 572 newModes.SetMode(modes.Mode(mode), true) 573 } 574 channel.stateMutex.Lock() 575 defer channel.stateMutex.Unlock() 576 if _, ok := channel.members[client]; !ok { 577 return 578 } 579 memberData := channel.members[client] 580 memberData.modes = newModes 581 memberData.joinTime = status.JoinTime 582 channel.members[client] = memberData 583} 584 585func (channel *Channel) ClientHasPrivsOver(client *Client, target *Client) bool { 586 channel.stateMutex.RLock() 587 founder := channel.registeredFounder 588 clientModes := channel.members[client].modes 589 targetModes := channel.members[target].modes 590 channel.stateMutex.RUnlock() 591 592 if founder != "" { 593 if founder == client.Account() { 594 return true // #950: founder can take any privileged action without actually having +q 595 } else if founder == target.Account() { 596 return false // conversely, only the founder can kick the founder 597 } 598 } 599 600 return channelUserModeHasPrivsOver(clientModes.HighestChannelUserMode(), targetModes.HighestChannelUserMode()) 601} 602 603func (channel *Channel) hasClient(client *Client) bool { 604 channel.stateMutex.RLock() 605 _, present := channel.members[client] 606 channel.stateMutex.RUnlock() 607 return present 608} 609 610// <mode> <mode params> 611func (channel *Channel) modeStrings(client *Client) (result []string) { 612 hasPrivs := client.HasRoleCapabs("sajoin") 613 614 channel.stateMutex.RLock() 615 defer channel.stateMutex.RUnlock() 616 617 isMember := hasPrivs || channel.members.Has(client) 618 showKey := isMember && (channel.key != "") 619 showUserLimit := channel.userLimit > 0 620 showForward := channel.forward != "" 621 622 var mods strings.Builder 623 mods.WriteRune('+') 624 625 // flags with args 626 if showKey { 627 mods.WriteRune(rune(modes.Key)) 628 } 629 if showUserLimit { 630 mods.WriteRune(rune(modes.UserLimit)) 631 } 632 if showForward { 633 mods.WriteRune(rune(modes.Forward)) 634 } 635 636 for _, m := range channel.flags.AllModes() { 637 mods.WriteRune(rune(m)) 638 } 639 640 result = []string{mods.String()} 641 642 // args for flags with args: The order must match above to keep 643 // positional arguments in place. 644 if showKey { 645 result = append(result, channel.key) 646 } 647 if showUserLimit { 648 result = append(result, strconv.Itoa(channel.userLimit)) 649 } 650 if showForward { 651 result = append(result, channel.forward) 652 } 653 654 return 655} 656 657func (channel *Channel) IsEmpty() bool { 658 channel.stateMutex.RLock() 659 defer channel.stateMutex.RUnlock() 660 return len(channel.members) == 0 661} 662 663// figure out where history is being stored: persistent, ephemeral, or neither 664// target is only needed if we're doing persistent history 665func (channel *Channel) historyStatus(config *Config) (status HistoryStatus, target string, restrictions HistoryCutoff) { 666 if !config.History.Enabled { 667 return HistoryDisabled, "", HistoryCutoffNone 668 } 669 670 channel.stateMutex.RLock() 671 target = channel.nameCasefolded 672 settings := channel.settings 673 registered := channel.registeredFounder != "" 674 channel.stateMutex.RUnlock() 675 676 restrictions = settings.QueryCutoff 677 if restrictions == HistoryCutoffDefault { 678 restrictions = config.History.Restrictions.queryCutoff 679 } 680 681 return channelHistoryStatus(config, registered, settings.History), target, restrictions 682} 683 684func (channel *Channel) joinTimeCutoff(client *Client) (present bool, cutoff time.Time) { 685 account := client.Account() 686 687 channel.stateMutex.RLock() 688 defer channel.stateMutex.RUnlock() 689 if data, ok := channel.members[client]; ok { 690 present = true 691 // report a cutoff of zero, i.e., no restriction, if the user is privileged 692 if !((account != "" && account == channel.registeredFounder) || data.modes.HasMode(modes.ChannelFounder) || data.modes.HasMode(modes.ChannelAdmin) || data.modes.HasMode(modes.ChannelOperator)) { 693 cutoff = time.Unix(0, data.joinTime) 694 } 695 } 696 return 697} 698 699func channelHistoryStatus(config *Config, registered bool, storedStatus HistoryStatus) (result HistoryStatus) { 700 if !config.History.Enabled { 701 return HistoryDisabled 702 } 703 704 // ephemeral history: either the channel owner explicitly set the ephemeral preference, 705 // or persistent history is disabled for unregistered channels 706 if registered { 707 return historyEnabled(config.History.Persistent.RegisteredChannels, storedStatus) 708 } else { 709 if config.History.Persistent.UnregisteredChannels { 710 return HistoryPersistent 711 } else { 712 return HistoryEphemeral 713 } 714 } 715} 716 717func (channel *Channel) AddHistoryItem(item history.Item, account string) (err error) { 718 if !itemIsStorable(&item, channel.server.Config()) { 719 return 720 } 721 722 status, target, _ := channel.historyStatus(channel.server.Config()) 723 if status == HistoryPersistent { 724 err = channel.server.historyDB.AddChannelItem(target, item, account) 725 } else if status == HistoryEphemeral { 726 channel.history.Add(item) 727 } 728 return 729} 730 731// Join joins the given client to this channel (if they can be joined). 732func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *ResponseBuffer) (joinErr error, forward string) { 733 details := client.Details() 734 isBot := client.HasMode(modes.Bot) 735 736 channel.stateMutex.RLock() 737 chname := channel.name 738 chcfname := channel.nameCasefolded 739 founder := channel.registeredFounder 740 createdAt := channel.createdTime 741 chkey := channel.key 742 limit := channel.userLimit 743 chcount := len(channel.members) 744 _, alreadyJoined := channel.members[client] 745 persistentMode := channel.accountToUMode[details.account] 746 forward = channel.forward 747 channel.stateMutex.RUnlock() 748 749 if alreadyJoined { 750 // no message needs to be sent 751 return nil, "" 752 } 753 754 // 0. SAJOIN always succeeds 755 // 1. the founder can always join (even if they disabled auto +q on join) 756 // 2. anyone who automatically receives halfop or higher can always join 757 // 3. people invited with INVITE can join 758 hasPrivs := isSajoin || (founder != "" && founder == details.account) || 759 (persistentMode != 0 && persistentMode != modes.Voice) || 760 client.CheckInvited(chcfname, createdAt) 761 if !hasPrivs { 762 if limit != 0 && chcount >= limit { 763 return errLimitExceeded, forward 764 } 765 766 if chkey != "" && !utils.SecretTokensMatch(chkey, key) { 767 return errWrongChannelKey, forward 768 } 769 770 if channel.flags.HasMode(modes.InviteOnly) && 771 !channel.lists[modes.InviteMask].Match(details.nickMaskCasefolded) { 772 return errInviteOnly, forward 773 } 774 775 if channel.lists[modes.BanMask].Match(details.nickMaskCasefolded) && 776 !channel.lists[modes.ExceptMask].Match(details.nickMaskCasefolded) && 777 !channel.lists[modes.InviteMask].Match(details.nickMaskCasefolded) { 778 // do not forward people who are banned: 779 return errBanned, "" 780 } 781 782 if details.account == "" && 783 (channel.flags.HasMode(modes.RegisteredOnly) || channel.server.Defcon() <= 2) { 784 return errRegisteredOnly, forward 785 } 786 } 787 788 if joinErr := client.addChannel(channel, rb == nil); joinErr != nil { 789 return joinErr, "" 790 } 791 792 client.server.logger.Debug("channels", fmt.Sprintf("%s joined channel %s", details.nick, chname)) 793 794 givenMode := func() (givenMode modes.Mode) { 795 channel.joinPartMutex.Lock() 796 defer channel.joinPartMutex.Unlock() 797 798 func() { 799 channel.stateMutex.Lock() 800 defer channel.stateMutex.Unlock() 801 802 channel.members.Add(client) 803 firstJoin := len(channel.members) == 1 804 newChannel := firstJoin && channel.registeredFounder == "" 805 if newChannel { 806 givenMode = modes.ChannelOperator 807 } else { 808 givenMode = persistentMode 809 } 810 if givenMode != 0 { 811 channel.members[client].modes.SetMode(givenMode, true) 812 } 813 }() 814 815 channel.regenerateMembersCache() 816 817 return 818 }() 819 820 var message utils.SplitMessage 821 respectAuditorium := givenMode == modes.Mode(0) && channel.flags.HasMode(modes.Auditorium) 822 message = utils.MakeMessage("") 823 // no history item for fake persistent joins 824 if rb != nil && !respectAuditorium { 825 histItem := history.Item{ 826 Type: history.Join, 827 Nick: details.nickMask, 828 AccountName: details.accountName, 829 Message: message, 830 IsBot: isBot, 831 } 832 histItem.Params[0] = details.realname 833 channel.AddHistoryItem(histItem, details.account) 834 } 835 836 if rb == nil { 837 return nil, "" 838 } 839 840 var modestr string 841 if givenMode != 0 { 842 modestr = fmt.Sprintf("+%v", givenMode) 843 } 844 845 // cache the most common case (JOIN without extended-join) 846 var cache MessageCache 847 cache.Initialize(channel.server, message.Time, message.Msgid, details.nickMask, details.accountName, isBot, nil, "JOIN", chname) 848 isAway, awayMessage := client.Away() 849 for _, member := range channel.Members() { 850 if respectAuditorium { 851 channel.stateMutex.RLock() 852 memberData, ok := channel.members[member] 853 channel.stateMutex.RUnlock() 854 if !ok || memberData.modes.HighestChannelUserMode() == modes.Mode(0) { 855 continue 856 } 857 } 858 for _, session := range member.Sessions() { 859 if session == rb.session { 860 continue 861 } else if client == session.client { 862 channel.playJoinForSession(session) 863 continue 864 } 865 if session.capabilities.Has(caps.ExtendedJoin) { 866 session.sendFromClientInternal(false, message.Time, message.Msgid, details.nickMask, details.accountName, isBot, nil, "JOIN", chname, details.accountName, details.realname) 867 } else { 868 cache.Send(session) 869 } 870 if givenMode != 0 { 871 session.Send(nil, client.server.name, "MODE", chname, modestr, details.nick) 872 } 873 if isAway && session.capabilities.Has(caps.AwayNotify) { 874 session.sendFromClientInternal(false, time.Time{}, "", details.nickMask, details.accountName, isBot, nil, "AWAY", awayMessage) 875 } 876 } 877 } 878 879 if rb.session.capabilities.Has(caps.ExtendedJoin) { 880 rb.AddFromClient(message.Time, message.Msgid, details.nickMask, details.accountName, isBot, nil, "JOIN", chname, details.accountName, details.realname) 881 } else { 882 rb.AddFromClient(message.Time, message.Msgid, details.nickMask, details.accountName, isBot, nil, "JOIN", chname) 883 } 884 885 if rb.session.client == client { 886 // don't send topic and names for a SAJOIN of a different client 887 channel.SendTopic(client, rb, false) 888 channel.Names(client, rb) 889 } else { 890 // ensure that SAJOIN sends a MODE line to the originating client, if applicable 891 if givenMode != 0 { 892 rb.Add(nil, client.server.name, "MODE", chname, modestr, details.nick) 893 } 894 } 895 896 // TODO #259 can be implemented as Flush(false) (i.e., nonblocking) while holding joinPartMutex 897 rb.Flush(true) 898 899 channel.autoReplayHistory(client, rb, message.Msgid) 900 return nil, "" 901} 902 903func (channel *Channel) autoReplayHistory(client *Client, rb *ResponseBuffer, skipMsgid string) { 904 // autoreplay any messages as necessary 905 var items []history.Item 906 907 hasAutoreplayTimestamps := false 908 var start, end time.Time 909 if rb.session.zncPlaybackTimes.ValidFor(channel.NameCasefolded()) { 910 hasAutoreplayTimestamps = true 911 start, end = rb.session.zncPlaybackTimes.start, rb.session.zncPlaybackTimes.end 912 } else if !rb.session.autoreplayMissedSince.IsZero() { 913 // we already checked for history caps in `playReattachMessages` 914 hasAutoreplayTimestamps = true 915 start = time.Now().UTC() 916 end = rb.session.autoreplayMissedSince 917 } 918 919 if hasAutoreplayTimestamps { 920 _, seq, _ := channel.server.GetHistorySequence(channel, client, "") 921 if seq != nil { 922 zncMax := channel.server.Config().History.ZNCMax 923 items, _ = seq.Between(history.Selector{Time: start}, history.Selector{Time: end}, zncMax) 924 } 925 } else if !rb.session.HasHistoryCaps() { 926 var replayLimit int 927 customReplayLimit := client.AccountSettings().AutoreplayLines 928 if customReplayLimit != nil { 929 replayLimit = *customReplayLimit 930 maxLimit := channel.server.Config().History.ChathistoryMax 931 if maxLimit < replayLimit { 932 replayLimit = maxLimit 933 } 934 } else { 935 replayLimit = channel.server.Config().History.AutoreplayOnJoin 936 } 937 if 0 < replayLimit { 938 _, seq, _ := channel.server.GetHistorySequence(channel, client, "") 939 if seq != nil { 940 items, _ = seq.Between(history.Selector{}, history.Selector{}, replayLimit) 941 } 942 } 943 } 944 // remove the client's own JOIN line from the replay 945 numItems := len(items) 946 for i := len(items) - 1; 0 <= i; i-- { 947 if items[i].Message.Msgid == skipMsgid { 948 // zero'ed items will not be replayed because their `Type` field is not recognized 949 items[i] = history.Item{} 950 numItems-- 951 break 952 } 953 } 954 if 0 < numItems { 955 channel.replayHistoryItems(rb, items, false) 956 rb.Flush(true) 957 } 958} 959 960// plays channel join messages (the JOIN line, topic, and names) to a session. 961// this is used when attaching a new session to an existing client that already has 962// channels, and also when one session of a client initiates a JOIN and the other 963// sessions need to receive the state change 964func (channel *Channel) playJoinForSession(session *Session) { 965 client := session.client 966 sessionRb := NewResponseBuffer(session) 967 details := client.Details() 968 if session.capabilities.Has(caps.ExtendedJoin) { 969 sessionRb.Add(nil, details.nickMask, "JOIN", channel.Name(), details.accountName, details.realname) 970 } else { 971 sessionRb.Add(nil, details.nickMask, "JOIN", channel.Name()) 972 } 973 channel.SendTopic(client, sessionRb, false) 974 channel.Names(client, sessionRb) 975 sessionRb.Send(false) 976} 977 978// Part parts the given client from this channel, with the given message. 979func (channel *Channel) Part(client *Client, message string, rb *ResponseBuffer) { 980 channel.stateMutex.RLock() 981 chname := channel.name 982 clientData, ok := channel.members[client] 983 channel.stateMutex.RUnlock() 984 985 if !ok { 986 rb.Add(nil, client.server.name, ERR_NOTONCHANNEL, client.Nick(), chname, client.t("You're not on that channel")) 987 return 988 } 989 990 channel.Quit(client) 991 992 splitMessage := utils.MakeMessage(message) 993 994 details := client.Details() 995 isBot := client.HasMode(modes.Bot) 996 params := make([]string, 1, 2) 997 params[0] = chname 998 if message != "" { 999 params = append(params, message) 1000 } 1001 respectAuditorium := channel.flags.HasMode(modes.Auditorium) && 1002 clientData.modes.HighestChannelUserMode() == modes.Mode(0) 1003 var cache MessageCache 1004 cache.Initialize(channel.server, splitMessage.Time, splitMessage.Msgid, details.nickMask, details.accountName, isBot, nil, "PART", params...) 1005 for _, member := range channel.Members() { 1006 if respectAuditorium { 1007 channel.stateMutex.RLock() 1008 memberData, ok := channel.members[member] 1009 channel.stateMutex.RUnlock() 1010 if !ok || memberData.modes.HighestChannelUserMode() == modes.Mode(0) { 1011 continue 1012 } 1013 } 1014 for _, session := range member.Sessions() { 1015 cache.Send(session) 1016 } 1017 } 1018 rb.AddFromClient(splitMessage.Time, splitMessage.Msgid, details.nickMask, details.accountName, isBot, nil, "PART", params...) 1019 for _, session := range client.Sessions() { 1020 if session != rb.session { 1021 session.sendFromClientInternal(false, splitMessage.Time, splitMessage.Msgid, details.nickMask, details.accountName, isBot, nil, "PART", params...) 1022 } 1023 } 1024 1025 if !respectAuditorium { 1026 channel.AddHistoryItem(history.Item{ 1027 Type: history.Part, 1028 Nick: details.nickMask, 1029 AccountName: details.accountName, 1030 Message: splitMessage, 1031 IsBot: isBot, 1032 }, details.account) 1033 } 1034 1035 client.server.logger.Debug("channels", fmt.Sprintf("%s left channel %s", details.nick, chname)) 1036} 1037 1038func (channel *Channel) replayHistoryItems(rb *ResponseBuffer, items []history.Item, chathistoryCommand bool) { 1039 // send an empty batch if necessary, as per the CHATHISTORY spec 1040 chname := channel.Name() 1041 client := rb.target 1042 eventPlayback := rb.session.capabilities.Has(caps.EventPlayback) 1043 extendedJoin := rb.session.capabilities.Has(caps.ExtendedJoin) 1044 var playJoinsAsPrivmsg bool 1045 if !eventPlayback { 1046 if chathistoryCommand { 1047 playJoinsAsPrivmsg = true 1048 } else { 1049 switch client.AccountSettings().ReplayJoins { 1050 case ReplayJoinsCommandsOnly: 1051 playJoinsAsPrivmsg = false 1052 case ReplayJoinsAlways: 1053 playJoinsAsPrivmsg = true 1054 } 1055 } 1056 } 1057 1058 batchID := rb.StartNestedHistoryBatch(chname) 1059 defer rb.EndNestedBatch(batchID) 1060 1061 for _, item := range items { 1062 nick := NUHToNick(item.Nick) 1063 switch item.Type { 1064 case history.Privmsg: 1065 rb.AddSplitMessageFromClient(item.Nick, item.AccountName, item.IsBot, item.Tags, "PRIVMSG", chname, item.Message) 1066 case history.Notice: 1067 rb.AddSplitMessageFromClient(item.Nick, item.AccountName, item.IsBot, item.Tags, "NOTICE", chname, item.Message) 1068 case history.Tagmsg: 1069 if eventPlayback { 1070 rb.AddSplitMessageFromClient(item.Nick, item.AccountName, item.IsBot, item.Tags, "TAGMSG", chname, item.Message) 1071 } else if chathistoryCommand { 1072 // #1676, we have to send something here or else it breaks pagination 1073 rb.AddFromClient(item.Message.Time, history.HistservMungeMsgid(item.Message.Msgid), histservService.prefix, "*", false, nil, "PRIVMSG", chname, fmt.Sprintf(client.t("%s sent a TAGMSG"), nick)) 1074 } 1075 case history.Join: 1076 if eventPlayback { 1077 if extendedJoin { 1078 rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, item.IsBot, nil, "JOIN", chname, item.AccountName, item.Params[0]) 1079 } else { 1080 rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, item.IsBot, nil, "JOIN", chname) 1081 } 1082 } else { 1083 if !playJoinsAsPrivmsg { 1084 continue // #474 1085 } 1086 var message string 1087 if item.AccountName == "*" { 1088 message = fmt.Sprintf(client.t("%s joined the channel"), nick) 1089 } else { 1090 message = fmt.Sprintf(client.t("%[1]s [account: %[2]s] joined the channel"), nick, item.AccountName) 1091 } 1092 rb.AddFromClient(item.Message.Time, history.HistservMungeMsgid(item.Message.Msgid), histservService.prefix, "*", false, nil, "PRIVMSG", chname, message) 1093 } 1094 case history.Part: 1095 if eventPlayback { 1096 rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, item.IsBot, nil, "PART", chname, item.Message.Message) 1097 } else { 1098 if !playJoinsAsPrivmsg { 1099 continue // #474 1100 } 1101 message := fmt.Sprintf(client.t("%[1]s left the channel (%[2]s)"), nick, item.Message.Message) 1102 rb.AddFromClient(item.Message.Time, history.HistservMungeMsgid(item.Message.Msgid), histservService.prefix, "*", false, nil, "PRIVMSG", chname, message) 1103 } 1104 case history.Kick: 1105 if eventPlayback { 1106 rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, item.IsBot, nil, "KICK", chname, item.Params[0], item.Message.Message) 1107 } else { 1108 message := fmt.Sprintf(client.t("%[1]s kicked %[2]s (%[3]s)"), nick, item.Params[0], item.Message.Message) 1109 rb.AddFromClient(item.Message.Time, history.HistservMungeMsgid(item.Message.Msgid), histservService.prefix, "*", false, nil, "PRIVMSG", chname, message) 1110 } 1111 case history.Quit: 1112 if eventPlayback { 1113 rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, item.IsBot, nil, "QUIT", item.Message.Message) 1114 } else { 1115 if !playJoinsAsPrivmsg { 1116 continue // #474 1117 } 1118 message := fmt.Sprintf(client.t("%[1]s quit (%[2]s)"), nick, item.Message.Message) 1119 rb.AddFromClient(item.Message.Time, history.HistservMungeMsgid(item.Message.Msgid), histservService.prefix, "*", false, nil, "PRIVMSG", chname, message) 1120 } 1121 case history.Nick: 1122 if eventPlayback { 1123 rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, item.IsBot, nil, "NICK", item.Params[0]) 1124 } else { 1125 message := fmt.Sprintf(client.t("%[1]s changed nick to %[2]s"), nick, item.Params[0]) 1126 rb.AddFromClient(item.Message.Time, history.HistservMungeMsgid(item.Message.Msgid), histservService.prefix, "*", false, nil, "PRIVMSG", chname, message) 1127 } 1128 case history.Topic: 1129 if eventPlayback { 1130 rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, item.IsBot, nil, "TOPIC", chname, item.Message.Message) 1131 } else { 1132 message := fmt.Sprintf(client.t("%[1]s set the channel topic to: %[2]s"), nick, item.Message.Message) 1133 rb.AddFromClient(item.Message.Time, history.HistservMungeMsgid(item.Message.Msgid), histservService.prefix, "*", false, nil, "PRIVMSG", chname, message) 1134 } 1135 case history.Mode: 1136 params := make([]string, len(item.Message.Split)+1) 1137 params[0] = chname 1138 for i, pair := range item.Message.Split { 1139 params[i+1] = pair.Message 1140 } 1141 if eventPlayback { 1142 rb.AddFromClient(item.Message.Time, item.Message.Msgid, item.Nick, item.AccountName, item.IsBot, nil, "MODE", params...) 1143 } else { 1144 message := fmt.Sprintf(client.t("%[1]s set channel modes: %[2]s"), nick, strings.Join(params[1:], " ")) 1145 rb.AddFromClient(item.Message.Time, history.HistservMungeMsgid(item.Message.Msgid), histservService.prefix, "*", false, nil, "PRIVMSG", chname, message) 1146 } 1147 } 1148 } 1149} 1150 1151// SendTopic sends the channel topic to the given client. 1152// `sendNoTopic` controls whether RPL_NOTOPIC is sent when the topic is unset 1153func (channel *Channel) SendTopic(client *Client, rb *ResponseBuffer, sendNoTopic bool) { 1154 channel.stateMutex.RLock() 1155 name := channel.name 1156 topic := channel.topic 1157 topicSetBy := channel.topicSetBy 1158 topicSetTime := channel.topicSetTime 1159 _, hasClient := channel.members[client] 1160 channel.stateMutex.RUnlock() 1161 1162 if !hasClient { 1163 rb.Add(nil, client.server.name, ERR_NOTONCHANNEL, client.Nick(), channel.name, client.t("You're not on that channel")) 1164 return 1165 } 1166 1167 if topic == "" { 1168 if sendNoTopic { 1169 rb.Add(nil, client.server.name, RPL_NOTOPIC, client.nick, name, client.t("No topic is set")) 1170 } 1171 return 1172 } 1173 1174 rb.Add(nil, client.server.name, RPL_TOPIC, client.nick, name, topic) 1175 rb.Add(nil, client.server.name, RPL_TOPICTIME, client.nick, name, topicSetBy, strconv.FormatInt(topicSetTime.Unix(), 10)) 1176} 1177 1178// SetTopic sets the topic of this channel, if the client is allowed to do so. 1179func (channel *Channel) SetTopic(client *Client, topic string, rb *ResponseBuffer) { 1180 if !channel.hasClient(client) { 1181 rb.Add(nil, client.server.name, ERR_NOTONCHANNEL, client.Nick(), channel.Name(), client.t("You're not on that channel")) 1182 return 1183 } 1184 1185 if channel.flags.HasMode(modes.OpOnlyTopic) && !(channel.ClientIsAtLeast(client, modes.Halfop) || client.HasRoleCapabs("samode")) { 1186 rb.Add(nil, client.server.name, ERR_CHANOPRIVSNEEDED, client.Nick(), channel.Name(), client.t("You're not a channel operator")) 1187 return 1188 } 1189 1190 topic = ircutils.TruncateUTF8Safe(topic, client.server.Config().Limits.TopicLen) 1191 1192 channel.stateMutex.Lock() 1193 chname := channel.name 1194 channel.topic = topic 1195 channel.topicSetBy = client.nickMaskString 1196 channel.topicSetTime = time.Now().UTC() 1197 channel.stateMutex.Unlock() 1198 1199 details := client.Details() 1200 isBot := client.HasMode(modes.Bot) 1201 message := utils.MakeMessage(topic) 1202 rb.AddFromClient(message.Time, message.Msgid, details.nickMask, details.accountName, isBot, nil, "TOPIC", chname, topic) 1203 for _, member := range channel.Members() { 1204 for _, session := range member.Sessions() { 1205 if session != rb.session { 1206 session.sendFromClientInternal(false, message.Time, message.Msgid, details.nickMask, details.accountName, isBot, nil, "TOPIC", chname, topic) 1207 } 1208 } 1209 } 1210 1211 channel.AddHistoryItem(history.Item{ 1212 Type: history.Topic, 1213 Nick: details.nickMask, 1214 AccountName: details.accountName, 1215 Message: message, 1216 IsBot: isBot, 1217 }, details.account) 1218 1219 channel.MarkDirty(IncludeTopic) 1220} 1221 1222// CanSpeak returns true if the client can speak on this channel, otherwise it returns false along with the channel mode preventing the client from speaking. 1223func (channel *Channel) CanSpeak(client *Client) (bool, modes.Mode) { 1224 channel.stateMutex.RLock() 1225 memberData, hasClient := channel.members[client] 1226 channel.stateMutex.RUnlock() 1227 clientModes := memberData.modes 1228 1229 if !hasClient && channel.flags.HasMode(modes.NoOutside) { 1230 // TODO: enforce regular +b bans on -n channels? 1231 return false, modes.NoOutside 1232 } 1233 if channel.isMuted(client) && clientModes.HighestChannelUserMode() == modes.Mode(0) { 1234 return false, modes.BanMask 1235 } 1236 if channel.flags.HasMode(modes.Moderated) && clientModes.HighestChannelUserMode() == modes.Mode(0) { 1237 return false, modes.Moderated 1238 } 1239 if channel.flags.HasMode(modes.RegisteredOnly) && client.Account() == "" && 1240 clientModes.HighestChannelUserMode() == modes.Mode(0) { 1241 return false, modes.RegisteredOnly 1242 } 1243 if channel.flags.HasMode(modes.RegisteredOnlySpeak) && client.Account() == "" && 1244 clientModes.HighestChannelUserMode() == modes.Mode(0) { 1245 return false, modes.RegisteredOnlySpeak 1246 } 1247 return true, modes.Mode('?') 1248} 1249 1250func (channel *Channel) isMuted(client *Client) bool { 1251 muteRe := channel.lists[modes.BanMask].MuteRegexp() 1252 if muteRe == nil { 1253 return false 1254 } 1255 nuh := client.NickMaskCasefolded() 1256 return muteRe.MatchString(nuh) && !channel.lists[modes.ExceptMask].MatchMute(nuh) 1257} 1258 1259func (channel *Channel) relayNickMuted(relayNick string) bool { 1260 relayNUH := fmt.Sprintf("%s!*@*", relayNick) 1261 return channel.lists[modes.BanMask].MatchMute(relayNUH) && 1262 !channel.lists[modes.ExceptMask].MatchMute(relayNUH) 1263} 1264 1265func msgCommandToHistType(command string) (history.ItemType, error) { 1266 switch command { 1267 case "PRIVMSG": 1268 return history.Privmsg, nil 1269 case "NOTICE": 1270 return history.Notice, nil 1271 case "TAGMSG": 1272 return history.Tagmsg, nil 1273 default: 1274 return history.ItemType(0), errInvalidParams 1275 } 1276} 1277 1278func (channel *Channel) SendSplitMessage(command string, minPrefixMode modes.Mode, clientOnlyTags map[string]string, client *Client, message utils.SplitMessage, rb *ResponseBuffer) { 1279 histType, err := msgCommandToHistType(command) 1280 if err != nil { 1281 return 1282 } 1283 1284 if canSpeak, mode := channel.CanSpeak(client); !canSpeak { 1285 if histType != history.Notice { 1286 rb.Add(nil, client.server.name, ERR_CANNOTSENDTOCHAN, client.Nick(), channel.Name(), fmt.Sprintf(client.t("Cannot send to channel (+%s)"), mode)) 1287 } 1288 return 1289 } 1290 1291 isCTCP := message.IsRestrictedCTCPMessage() 1292 if isCTCP && channel.flags.HasMode(modes.NoCTCP) { 1293 if histType != history.Notice { 1294 rb.Add(nil, client.server.name, ERR_CANNOTSENDTOCHAN, client.Nick(), channel.Name(), fmt.Sprintf(client.t("Cannot send to channel (+%s)"), "C")) 1295 } 1296 return 1297 } 1298 1299 details := client.Details() 1300 isBot := client.HasMode(modes.Bot) 1301 chname := channel.Name() 1302 1303 if !client.server.Config().Server.Compatibility.allowTruncation { 1304 if !validateSplitMessageLen(histType, details.nickMask, chname, message) { 1305 rb.Add(nil, client.server.name, ERR_INPUTTOOLONG, details.nick, client.t("Line too long to be relayed without truncation")) 1306 return 1307 } 1308 } 1309 1310 // STATUSMSG targets are prefixed with the supplied min-prefix, e.g., @#channel 1311 if minPrefixMode != modes.Mode(0) { 1312 chname = fmt.Sprintf("%s%s", modes.ChannelModePrefixes[minPrefixMode], chname) 1313 } 1314 1315 if channel.flags.HasMode(modes.OpModerated) { 1316 channel.stateMutex.RLock() 1317 cuData := channel.members[client] 1318 channel.stateMutex.RUnlock() 1319 if cuData.modes.HighestChannelUserMode() == modes.Mode(0) { 1320 // max(statusmsg_minmode, halfop) 1321 if minPrefixMode == modes.Mode(0) || minPrefixMode == modes.Voice { 1322 minPrefixMode = modes.Halfop 1323 } 1324 } 1325 } 1326 1327 // send echo-message 1328 rb.addEchoMessage(clientOnlyTags, details.nickMask, details.accountName, command, chname, message) 1329 1330 var cache MessageCache 1331 cache.InitializeSplitMessage(channel.server, details.nickMask, details.accountName, isBot, clientOnlyTags, command, chname, message) 1332 for _, member := range channel.Members() { 1333 if minPrefixMode != modes.Mode(0) && !channel.ClientIsAtLeast(member, minPrefixMode) { 1334 // STATUSMSG or OpModerated 1335 continue 1336 } 1337 1338 for _, session := range member.Sessions() { 1339 if session == rb.session { 1340 continue // we already sent echo-message, if applicable 1341 } 1342 1343 if isCTCP && session.isTor { 1344 continue // #753 1345 } 1346 1347 cache.Send(session) 1348 } 1349 } 1350 1351 // #959: don't save STATUSMSG (or OpModerated) 1352 if minPrefixMode == modes.Mode(0) { 1353 channel.AddHistoryItem(history.Item{ 1354 Type: histType, 1355 Message: message, 1356 Nick: details.nickMask, 1357 AccountName: details.accountName, 1358 Tags: clientOnlyTags, 1359 IsBot: isBot, 1360 }, details.account) 1361 } 1362} 1363 1364func (channel *Channel) applyModeToMember(client *Client, change modes.ModeChange, rb *ResponseBuffer) (applied bool, result modes.ModeChange) { 1365 target := channel.server.clients.Get(change.Arg) 1366 if target == nil { 1367 rb.Add(nil, client.server.name, ERR_NOSUCHNICK, client.Nick(), utils.SafeErrorParam(change.Arg), client.t("No such nick")) 1368 return 1369 } 1370 change.Arg = target.Nick() 1371 1372 channel.stateMutex.Lock() 1373 memberData, exists := channel.members[target] 1374 if exists { 1375 if memberData.modes.SetMode(change.Mode, change.Op == modes.Add) { 1376 applied = true 1377 result = change 1378 } 1379 } 1380 channel.stateMutex.Unlock() 1381 1382 if !exists { 1383 rb.Add(nil, client.server.name, ERR_USERNOTINCHANNEL, client.Nick(), channel.Name(), client.t("They aren't on that channel")) 1384 } 1385 if applied { 1386 target.markDirty(IncludeChannels) 1387 } 1388 return 1389} 1390 1391// ShowMaskList shows the given list to the client. 1392func (channel *Channel) ShowMaskList(client *Client, mode modes.Mode, rb *ResponseBuffer) { 1393 // choose appropriate modes 1394 var rpllist, rplendoflist string 1395 if mode == modes.BanMask { 1396 rpllist = RPL_BANLIST 1397 rplendoflist = RPL_ENDOFBANLIST 1398 } else if mode == modes.ExceptMask { 1399 rpllist = RPL_EXCEPTLIST 1400 rplendoflist = RPL_ENDOFEXCEPTLIST 1401 } else if mode == modes.InviteMask { 1402 rpllist = RPL_INVITELIST 1403 rplendoflist = RPL_ENDOFINVITELIST 1404 } 1405 1406 nick := client.Nick() 1407 chname := channel.Name() 1408 for mask, info := range channel.lists[mode].Masks() { 1409 rb.Add(nil, client.server.name, rpllist, nick, chname, mask, info.CreatorNickmask, strconv.FormatInt(info.TimeCreated.Unix(), 10)) 1410 } 1411 1412 rb.Add(nil, client.server.name, rplendoflist, nick, chname, client.t("End of list")) 1413} 1414 1415// Quit removes the given client from the channel 1416func (channel *Channel) Quit(client *Client) { 1417 channelEmpty := func() bool { 1418 channel.joinPartMutex.Lock() 1419 defer channel.joinPartMutex.Unlock() 1420 1421 channel.stateMutex.Lock() 1422 channel.members.Remove(client) 1423 channelEmpty := len(channel.members) == 0 1424 channel.stateMutex.Unlock() 1425 channel.regenerateMembersCache() 1426 return channelEmpty 1427 }() 1428 1429 if channelEmpty { 1430 client.server.channels.Cleanup(channel) 1431 } 1432 client.removeChannel(channel) 1433} 1434 1435func (channel *Channel) Kick(client *Client, target *Client, comment string, rb *ResponseBuffer, hasPrivs bool) { 1436 if !hasPrivs { 1437 if !channel.ClientHasPrivsOver(client, target) { 1438 rb.Add(nil, client.server.name, ERR_CHANOPRIVSNEEDED, client.Nick(), channel.Name(), client.t("You don't have enough channel privileges")) 1439 return 1440 } 1441 } 1442 if !channel.hasClient(target) { 1443 rb.Add(nil, client.server.name, ERR_USERNOTINCHANNEL, client.Nick(), channel.Name(), client.t("They aren't on that channel")) 1444 return 1445 } 1446 1447 comment = ircutils.TruncateUTF8Safe(comment, channel.server.Config().Limits.KickLen) 1448 1449 message := utils.MakeMessage(comment) 1450 details := client.Details() 1451 isBot := client.HasMode(modes.Bot) 1452 1453 targetNick := target.Nick() 1454 chname := channel.Name() 1455 for _, member := range channel.Members() { 1456 for _, session := range member.Sessions() { 1457 if session != rb.session { 1458 session.sendFromClientInternal(false, message.Time, message.Msgid, details.nickMask, details.accountName, isBot, nil, "KICK", chname, targetNick, comment) 1459 } 1460 } 1461 } 1462 rb.AddFromClient(message.Time, message.Msgid, details.nickMask, details.accountName, isBot, nil, "KICK", chname, targetNick, comment) 1463 1464 histItem := history.Item{ 1465 Type: history.Kick, 1466 Nick: details.nickMask, 1467 AccountName: details.accountName, 1468 Message: message, 1469 IsBot: isBot, 1470 } 1471 histItem.Params[0] = targetNick 1472 channel.AddHistoryItem(histItem, details.account) 1473 1474 channel.Quit(target) 1475} 1476 1477// handle a purge: kick everyone off the channel, clean up all the pointers between 1478// *Channel and *Client 1479func (channel *Channel) Purge(source string) { 1480 if source == "" { 1481 source = channel.server.name 1482 } 1483 1484 channel.stateMutex.Lock() 1485 chname := channel.name 1486 members := channel.membersCache 1487 channel.membersCache = nil 1488 channel.members = make(MemberSet) 1489 // TODO try to prevent Purge racing against (pending) Join? 1490 channel.stateMutex.Unlock() 1491 1492 now := time.Now().UTC() 1493 for _, member := range members { 1494 tnick := member.Nick() 1495 msgid := utils.GenerateSecretToken() 1496 for _, session := range member.Sessions() { 1497 session.sendFromClientInternal(false, now, msgid, source, "*", false, nil, "KICK", chname, tnick, member.t("This channel has been purged by the server administrators and cannot be used")) 1498 } 1499 member.removeChannel(channel) 1500 } 1501} 1502 1503// Invite invites the given client to the channel, if the inviter can do so. 1504func (channel *Channel) Invite(invitee *Client, inviter *Client, rb *ResponseBuffer) { 1505 channel.stateMutex.RLock() 1506 chname := channel.name 1507 chcfname := channel.nameCasefolded 1508 createdAt := channel.createdTime 1509 _, inviterPresent := channel.members[inviter] 1510 _, inviteePresent := channel.members[invitee] 1511 channel.stateMutex.RUnlock() 1512 1513 if !inviterPresent { 1514 rb.Add(nil, inviter.server.name, ERR_NOTONCHANNEL, inviter.Nick(), chname, inviter.t("You're not on that channel")) 1515 return 1516 } 1517 1518 inviteOnly := channel.flags.HasMode(modes.InviteOnly) 1519 if inviteOnly && !channel.ClientIsAtLeast(inviter, modes.ChannelOperator) { 1520 rb.Add(nil, inviter.server.name, ERR_CHANOPRIVSNEEDED, inviter.Nick(), chname, inviter.t("You're not a channel operator")) 1521 return 1522 } 1523 1524 if inviteePresent { 1525 rb.Add(nil, inviter.server.name, ERR_USERONCHANNEL, inviter.Nick(), invitee.Nick(), chname, inviter.t("User is already on that channel")) 1526 return 1527 } 1528 1529 if inviteOnly { 1530 invitee.Invite(chcfname, createdAt) 1531 } 1532 1533 details := inviter.Details() 1534 isBot := inviter.HasMode(modes.Bot) 1535 tDetails := invitee.Details() 1536 tnick := invitee.Nick() 1537 message := utils.MakeMessage(chname) 1538 item := history.Item{ 1539 Type: history.Invite, 1540 Message: message, 1541 } 1542 1543 for _, member := range channel.Members() { 1544 if member == inviter || member == invitee || !channel.ClientIsAtLeast(member, modes.Halfop) { 1545 continue 1546 } 1547 for _, session := range member.Sessions() { 1548 if session.capabilities.Has(caps.InviteNotify) { 1549 session.sendFromClientInternal(false, message.Time, message.Msgid, details.nickMask, details.accountName, isBot, nil, "INVITE", tnick, chname) 1550 } 1551 } 1552 } 1553 1554 rb.Add(nil, inviter.server.name, RPL_INVITING, details.nick, tnick, chname) 1555 for _, iSession := range invitee.Sessions() { 1556 iSession.sendFromClientInternal(false, message.Time, message.Msgid, details.nickMask, details.accountName, isBot, nil, "INVITE", tnick, chname) 1557 } 1558 if away, awayMessage := invitee.Away(); away { 1559 rb.Add(nil, inviter.server.name, RPL_AWAY, details.nick, tnick, awayMessage) 1560 } 1561 inviter.addHistoryItem(invitee, item, &details, &tDetails, channel.server.Config()) 1562} 1563 1564// Uninvite rescinds a channel invitation, if the inviter can do so. 1565func (channel *Channel) Uninvite(invitee *Client, inviter *Client, rb *ResponseBuffer) { 1566 if !channel.flags.HasMode(modes.InviteOnly) { 1567 rb.Add(nil, channel.server.name, "FAIL", "UNINVITE", "NOT_INVITE_ONLY", channel.Name(), inviter.t("Channel is not invite-only")) 1568 return 1569 } 1570 1571 if !channel.ClientIsAtLeast(inviter, modes.ChannelOperator) { 1572 rb.Add(nil, channel.server.name, "FAIL", "UNINVITE", "PRIVS_NEEDED", channel.Name(), inviter.t("You're not a channel operator")) 1573 return 1574 } 1575 1576 invitee.Uninvite(channel.NameCasefolded()) 1577 rb.Add(nil, channel.server.name, "UNINVITE", invitee.Nick(), channel.Name()) 1578} 1579 1580// returns who the client can "see" in the channel, respecting the auditorium mode 1581func (channel *Channel) auditoriumFriends(client *Client) (friends []*Client) { 1582 channel.stateMutex.RLock() 1583 defer channel.stateMutex.RUnlock() 1584 1585 clientData, found := channel.members[client] 1586 if !found { 1587 return // non-members have no friends 1588 } 1589 if !channel.flags.HasMode(modes.Auditorium) { 1590 return channel.membersCache // default behavior for members 1591 } 1592 if clientData.modes.HighestChannelUserMode() != modes.Mode(0) { 1593 return channel.membersCache // +v and up can see everyone in the auditorium 1594 } 1595 // without +v, your friends are those with +v and up 1596 for member, memberData := range channel.members { 1597 if memberData.modes.HighestChannelUserMode() != modes.Mode(0) { 1598 friends = append(friends, member) 1599 } 1600 } 1601 return 1602} 1603 1604// data for RPL_LIST 1605func (channel *Channel) listData() (memberCount int, name, topic string) { 1606 channel.stateMutex.RLock() 1607 defer channel.stateMutex.RUnlock() 1608 return len(channel.members), channel.name, channel.topic 1609} 1610