From 354da79bb2edaa1af7d909d2774e7d67eb4e198c Mon Sep 17 00:00:00 2001 From: Dimitri Sokolyuk Date: Tue, 23 Jan 2018 18:17:51 +0100 Subject: Add vendor --- vendor/github.com/fluffle/goirc/state/tracker.go | 369 +++++++++++++++++++++++ 1 file changed, 369 insertions(+) create mode 100644 vendor/github.com/fluffle/goirc/state/tracker.go (limited to 'vendor/github.com/fluffle/goirc/state/tracker.go') diff --git a/vendor/github.com/fluffle/goirc/state/tracker.go b/vendor/github.com/fluffle/goirc/state/tracker.go new file mode 100644 index 0000000..209c7cf --- /dev/null +++ b/vendor/github.com/fluffle/goirc/state/tracker.go @@ -0,0 +1,369 @@ +package state + +import ( + "github.com/fluffle/goirc/logging" + + "sync" +) + +// The state manager interface +type Tracker interface { + // Nick methods + NewNick(nick string) *Nick + GetNick(nick string) *Nick + ReNick(old, neu string) *Nick + DelNick(nick string) *Nick + NickInfo(nick, ident, host, name string) *Nick + NickModes(nick, modestr string) *Nick + // Channel methods + NewChannel(channel string) *Channel + GetChannel(channel string) *Channel + DelChannel(channel string) *Channel + Topic(channel, topic string) *Channel + ChannelModes(channel, modestr string, modeargs ...string) *Channel + // Information about ME! + Me() *Nick + // And the tracking operations + IsOn(channel, nick string) (*ChanPrivs, bool) + Associate(channel, nick string) *ChanPrivs + Dissociate(channel, nick string) + Wipe() + // The state tracker can output a debugging string + String() string +} + +// ... and a struct to implement it ... +type stateTracker struct { + // Map of channels we're on + chans map[string]*channel + // Map of nicks we know about + nicks map[string]*nick + + // We need to keep state on who we are :-) + me *nick + + // And we need to protect against data races *cough*. + mu sync.Mutex +} + +var _ Tracker = (*stateTracker)(nil) + +// ... and a constructor to make it ... +func NewTracker(mynick string) *stateTracker { + st := &stateTracker{ + chans: make(map[string]*channel), + nicks: make(map[string]*nick), + } + st.me = newNick(mynick) + st.nicks[mynick] = st.me + return st +} + +// ... and a method to wipe the state clean. +func (st *stateTracker) Wipe() { + st.mu.Lock() + defer st.mu.Unlock() + // Deleting all the channels implicitly deletes every nick but me. + for _, ch := range st.chans { + st.delChannel(ch) + } +} + +/******************************************************************************\ + * tracker methods to create/look up nicks/channels +\******************************************************************************/ + +// Creates a new nick, initialises it, and stores it so it +// can be properly tracked for state management purposes. +func (st *stateTracker) NewNick(n string) *Nick { + if n == "" { + logging.Warn("Tracker.NewNick(): Not tracking empty nick.") + return nil + } + st.mu.Lock() + defer st.mu.Unlock() + if _, ok := st.nicks[n]; ok { + logging.Warn("Tracker.NewNick(): %s already tracked.", n) + return nil + } + st.nicks[n] = newNick(n) + return st.nicks[n].Nick() +} + +// Returns a nick for the nick n, if we're tracking it. +func (st *stateTracker) GetNick(n string) *Nick { + st.mu.Lock() + defer st.mu.Unlock() + if nk, ok := st.nicks[n]; ok { + return nk.Nick() + } + return nil +} + +// Signals to the tracker that a nick should be tracked +// under a "neu" nick rather than the old one. +func (st *stateTracker) ReNick(old, neu string) *Nick { + st.mu.Lock() + defer st.mu.Unlock() + nk, ok := st.nicks[old] + if !ok { + logging.Warn("Tracker.ReNick(): %s not tracked.", old) + return nil + } + if _, ok := st.nicks[neu]; ok { + logging.Warn("Tracker.ReNick(): %s already exists.", neu) + return nil + } + + nk.nick = neu + delete(st.nicks, old) + st.nicks[neu] = nk + for ch, _ := range nk.chans { + // We also need to update the lookup maps of all the channels + // the nick is on, to keep things in sync. + delete(ch.lookup, old) + ch.lookup[neu] = nk + } + return nk.Nick() +} + +// Removes a nick from being tracked. +func (st *stateTracker) DelNick(n string) *Nick { + st.mu.Lock() + defer st.mu.Unlock() + if nk, ok := st.nicks[n]; ok { + if nk == st.me { + logging.Warn("Tracker.DelNick(): won't delete myself.") + return nil + } + st.delNick(nk) + return nk.Nick() + } + logging.Warn("Tracker.DelNick(): %s not tracked.", n) + return nil +} + +func (st *stateTracker) delNick(nk *nick) { + // st.mu lock held by DelNick, DelChannel or Wipe + if nk == st.me { + // Shouldn't get here => internal state tracking code is fubar. + logging.Error("Tracker.DelNick(): TRYING TO DELETE ME :-(") + return + } + delete(st.nicks, nk.nick) + for ch, _ := range nk.chans { + nk.delChannel(ch) + ch.delNick(nk) + if len(ch.nicks) == 0 { + // Deleting a nick from tracking shouldn't empty any channels as + // *we* should be on the channel with them to be tracking them. + logging.Error("Tracker.delNick(): deleting nick %s emptied "+ + "channel %s, this shouldn't happen!", nk.nick, ch.name) + } + } +} + +// Sets ident, host and "real" name for the nick. +func (st *stateTracker) NickInfo(n, ident, host, name string) *Nick { + st.mu.Lock() + defer st.mu.Unlock() + nk, ok := st.nicks[n] + if !ok { + return nil + } + nk.ident = ident + nk.host = host + nk.name = name + return nk.Nick() +} + +// Sets user modes for the nick. +func (st *stateTracker) NickModes(n, modes string) *Nick { + st.mu.Lock() + defer st.mu.Unlock() + nk, ok := st.nicks[n] + if !ok { + return nil + } + nk.parseModes(modes) + return nk.Nick() +} + +// Creates a new Channel, initialises it, and stores it so it +// can be properly tracked for state management purposes. +func (st *stateTracker) NewChannel(c string) *Channel { + if c == "" { + logging.Warn("Tracker.NewChannel(): Not tracking empty channel.") + return nil + } + st.mu.Lock() + defer st.mu.Unlock() + if _, ok := st.chans[c]; ok { + logging.Warn("Tracker.NewChannel(): %s already tracked.", c) + return nil + } + st.chans[c] = newChannel(c) + return st.chans[c].Channel() +} + +// Returns a Channel for the channel c, if we're tracking it. +func (st *stateTracker) GetChannel(c string) *Channel { + st.mu.Lock() + defer st.mu.Unlock() + if ch, ok := st.chans[c]; ok { + return ch.Channel() + } + return nil +} + +// Removes a Channel from being tracked. +func (st *stateTracker) DelChannel(c string) *Channel { + st.mu.Lock() + defer st.mu.Unlock() + if ch, ok := st.chans[c]; ok { + st.delChannel(ch) + return ch.Channel() + } + logging.Warn("Tracker.DelChannel(): %s not tracked.", c) + return nil +} + +func (st *stateTracker) delChannel(ch *channel) { + // st.mu lock held by DelChannel or Wipe + delete(st.chans, ch.name) + for nk, _ := range ch.nicks { + ch.delNick(nk) + nk.delChannel(ch) + if len(nk.chans) == 0 && nk != st.me { + // We're no longer in any channels with this nick. + st.delNick(nk) + } + } +} + +// Sets the topic of a channel. +func (st *stateTracker) Topic(c, topic string) *Channel { + st.mu.Lock() + defer st.mu.Unlock() + ch, ok := st.chans[c] + if !ok { + return nil + } + ch.topic = topic + return ch.Channel() +} + +// Sets modes for a channel, including privileges like +o. +func (st *stateTracker) ChannelModes(c, modes string, args ...string) *Channel { + st.mu.Lock() + defer st.mu.Unlock() + ch, ok := st.chans[c] + if !ok { + return nil + } + ch.parseModes(modes, args...) + return ch.Channel() +} + +// Returns the Nick the state tracker thinks is Me. +// NOTE: Nick() requires the mutex to be held. +func (st *stateTracker) Me() *Nick { + st.mu.Lock() + defer st.mu.Unlock() + return st.me.Nick() +} + +// Returns true if both the channel c and the nick n are tracked +// and the nick is associated with the channel. +func (st *stateTracker) IsOn(c, n string) (*ChanPrivs, bool) { + st.mu.Lock() + defer st.mu.Unlock() + nk, nok := st.nicks[n] + ch, cok := st.chans[c] + if nok && cok { + return nk.isOn(ch) + } + return nil, false +} + +// Associates an already known nick with an already known channel. +func (st *stateTracker) Associate(c, n string) *ChanPrivs { + st.mu.Lock() + defer st.mu.Unlock() + nk, nok := st.nicks[n] + ch, cok := st.chans[c] + + if !cok { + // As we can implicitly delete both nicks and channels from being + // tracked by dissociating one from the other, we should verify that + // we're not being passed an old Nick or Channel. + logging.Error("Tracker.Associate(): channel %s not found in "+ + "internal state.", c) + return nil + } else if !nok { + logging.Error("Tracker.Associate(): nick %s not found in "+ + "internal state.", n) + return nil + } else if _, ok := nk.isOn(ch); ok { + logging.Warn("Tracker.Associate(): %s already on %s.", + nk, ch) + return nil + } + cp := new(ChanPrivs) + ch.addNick(nk, cp) + nk.addChannel(ch, cp) + return cp.Copy() +} + +// Dissociates an already known nick from an already known channel. +// Does some tidying up to stop tracking nicks we're no longer on +// any common channels with, and channels we're no longer on. +func (st *stateTracker) Dissociate(c, n string) { + st.mu.Lock() + defer st.mu.Unlock() + nk, nok := st.nicks[n] + ch, cok := st.chans[c] + + if !cok { + // As we can implicitly delete both nicks and channels from being + // tracked by dissociating one from the other, we should verify that + // we're not being passed an old Nick or Channel. + logging.Error("Tracker.Dissociate(): channel %s not found in "+ + "internal state.", c) + } else if !nok { + logging.Error("Tracker.Dissociate(): nick %s not found in "+ + "internal state.", n) + } else if _, ok := nk.isOn(ch); !ok { + logging.Warn("Tracker.Dissociate(): %s not on %s.", + nk.nick, ch.name) + } else if nk == st.me { + // I'm leaving the channel for some reason, so it won't be tracked. + st.delChannel(ch) + } else { + // Remove the nick from the channel and the channel from the nick. + ch.delNick(nk) + nk.delChannel(ch) + if len(nk.chans) == 0 { + // We're no longer in any channels with this nick. + st.delNick(nk) + } + } +} + +func (st *stateTracker) String() string { + st.mu.Lock() + defer st.mu.Unlock() + str := "GoIRC Channels\n" + str += "--------------\n\n" + for _, ch := range st.chans { + str += ch.String() + "\n" + } + str += "GoIRC NickNames\n" + str += "---------------\n\n" + for _, n := range st.nicks { + if n != st.me { + str += n.String() + "\n" + } + } + return str +} -- cgit v1.2.3