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