func (p *Plugin) dumpBans(target string) { num := 0 // Fetch ban list banlist, err := p.mode.Bans(target) if err != nil { logging.Warn("Could not fetch ban list, old bans won't get handled") return } tbmgr := p.ensureTemporaryBanManager(target) // Save only bans from us for _, ban := range banlist { if ban.Nick != p.bot.Me().Nick { // Not a ban from us (going by the nickname at least) isOldHostmask := false for _, hostmask := range p.OldHostmasks { // TODO - Test this implementation hostmask = regexp.QuoteMeta(hostmask) hostmask = strings.Replace(hostmask, "\\*", ".*", -1) hostmask = strings.Replace(hostmask, "\\?", ".?", -1) if matched, err := regexp.MatchString(hostmask, ban.Src); matched { isOldHostmask = true break } else if err != nil { logging.Error("vpnbot.Plugin: dumpBans regular expression failed: %v", err) break } } if !isOldHostmask { // Not a ban from an old hostmask either continue } } if _, ok := tbmgr.Get(ban.Hostmask); ok { // We already have this ban saved continue } if err := tbmgr.Add(NewTemporaryBan( ban.Nick, ban.Hostmask, ban.Src, "Migrated old ban", 48*time.Hour+ban.Timestamp.Sub(time.Now()))); err != nil { logging.Warn("Could not migrate ban on %v: %v", ban.Hostmask, err) } num++ } if num > 0 { p.syncBans(target) logging.Info("Migrated %v bans", num) } }
// Removes a Nick from being tracked. func (st *stateTracker) DelNick(n string) { if nk, ok := st.nicks[n]; ok { if nk != st.me { st.delNick(nk) } else { logging.Warn("Tracker.DelNick(): won't delete myself.") } } else { logging.Warn("Tracker.DelNick(): %s not tracked.", n) } }
// 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 }
// 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() }
// 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() }
// Handle 352 who reply func (conn *Conn) h_352(line *Line) { nk := conn.st.GetNick(line.Args[5]) if nk == nil { logging.Warn("irc.352(): received WHO reply for unknown nick %s", line.Args[5]) return } if conn.Me().Equals(nk) { return } // XXX: do we care about the actual server the nick is on? // or the hop count to this server? // last arg contains "<hop count> <real name>" a := strings.SplitN(line.Args[len(line.Args)-1], " ", 2) conn.st.NickInfo(nk.Nick, line.Args[2], line.Args[3], a[1]) if idx := strings.Index(line.Args[6], "*"); idx != -1 { conn.st.NickModes(nk.Nick, "+o") } if idx := strings.Index(line.Args[6], "B"); idx != -1 { conn.st.NickModes(nk.Nick, "+B") } if idx := strings.Index(line.Args[6], "H"); idx != -1 { conn.st.NickModes(nk.Nick, "+i") } }
// Removes a Channel from being tracked. func (st *stateTracker) DelChannel(c string) { if ch, ok := st.chans[c]; ok { st.delChannel(ch) } else { logging.Warn("Tracker.DelChannel(): %s not tracked.", c) } }
func migrated(coll string) bool { var d done if err := ms.db.Get(K{{"collection", coll}}, &d); err != nil { logging.Warn("Checking migrated status for %q: %v", coll, err) } return d.Migrated }
// Handle 352 who reply func (conn *Conn) h_352(line *Line) { nk := conn.st.GetNick(line.Args[5]) if nk == nil { logging.Warn("irc.352(): received WHO reply for unknown nick %s", line.Args[5]) return } if nk == conn.Me() { return } nk.Ident = line.Args[2] nk.Host = line.Args[3] // XXX: do we care about the actual server the nick is on? // or the hop count to this server? // last arg contains "<hop count> <real name>" a := strings.SplitN(line.Args[len(line.Args)-1], " ", 2) nk.Name = a[1] if idx := strings.Index(line.Args[6], "*"); idx != -1 { nk.Modes.Oper = true } if idx := strings.Index(line.Args[6], "B"); idx != -1 { nk.Modes.Bot = true } if idx := strings.Index(line.Args[6], "H"); idx != -1 { nk.Modes.Invisible = true } }
// 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(ch *Channel, nk *Nick) { if ch == nil || nk == nil { logging.Error("Tracker.Dissociate(): passed nil values :-(") } else if _ch, ok := st.chans[ch.Name]; !ok || ch != _ch { // 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 "+ "(or differs from) internal state.", ch.Name) } else if _nk, ok := st.nicks[nk.Nick]; !ok || nk != _nk { logging.Error("Tracker.Dissociate(): nick %s not found in "+ "(or differs from) internal state.", nk.Nick) } 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) } } }
// Handle JOINs to channels to maintain state func (conn *Conn) h_JOIN(line *Line) { ch := conn.st.GetChannel(line.Args[0]) nk := conn.st.GetNick(line.Nick) if ch == nil { // first we've seen of this channel, so should be us joining it // NOTE this will also take care of nk == nil && ch == nil if nk != conn.cfg.Me { logging.Warn("irc.JOIN(): JOIN to unknown channel %s received "+ "from (non-me) nick %s", line.Args[0], line.Nick) return } ch = conn.st.NewChannel(line.Args[0]) // since we don't know much about this channel, ask server for info // we get the channel users automatically in 353 and the channel // topic in 332 on join, so we just need to get the modes conn.Mode(ch.Name) // sending a WHO for the channel is MUCH more efficient than // triggering a WHOIS on every nick from the 353 handler conn.Who(ch.Name) } if nk == nil { // this is the first we've seen of this nick nk = conn.st.NewNick(line.Nick) nk.Ident = line.Ident nk.Host = line.Host // since we don't know much about this nick, ask server for info conn.Who(nk.Nick) } // this takes care of both nick and channel linking \o/ conn.st.Associate(ch, nk) }
func (p *Plugin) syncBans(target string) { // Synchronize bans to file logging.Debug("Synchronizing temporary bans for %v to disk...", target) fn := p.getTempbansFilename(target) f, err := os.Create(fn) if err != nil { logging.Warn("Could not save temporary bans for %v: %v", fn, target, err.Error()) } defer f.Close() // Load temporary bans from this file if err := p.ensureTemporaryBanManager(target).Export(f); err != nil { logging.Warn("Could not save temporary bans: %v", err.Error()) } }
// Associates an already known nick with an already known channel. func (st *stateTracker) Associate(ch *Channel, nk *Nick) *ChanPrivs { if ch == nil || nk == nil { logging.Error("Tracker.Associate(): passed nil values :-(") return nil } else if _ch, ok := st.chans[ch.Name]; !ok || ch != _ch { // 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 "+ "(or differs from) internal state.", ch.Name) return nil } else if _nk, ok := st.nicks[nk.Nick]; !ok || nk != _nk { logging.Error("Tracker.Associate(): nick %s not found in "+ "(or differs from) internal state.", nk.Nick) return nil } else if _, ok := nk.IsOn(ch); ok { logging.Warn("Tracker.Associate(): %s already on %s.", nk.Nick, ch.Name) return nil } cp := new(ChanPrivs) ch.addNick(nk, cp) nk.addChannel(ch, cp) return cp }
// 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, c*k := st.chans[c] if !c*k { // 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) } } }
// 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, c*k := st.chans[c] if !c*k { // 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() }
// Handle 332 topic reply on join to channel func (conn *Conn) h_332(line *Line) { if ch := conn.st.GetChannel(line.Args[1]); ch != nil { ch.Topic = line.Args[2] } else { logging.Warn("irc.332(): received TOPIC value for unknown channel %s", line.Args[1]) } }
// Handle 324 mode reply func (conn *Conn) h_324(line *Line) { if ch := conn.st.GetChannel(line.Args[1]); ch != nil { ch.ParseModes(line.Args[2], line.Args[3:]...) } else { logging.Warn("irc.324(): received MODE settings for unknown channel %s", line.Args[1]) } }
// Handle 311 whois reply func (conn *Conn) h_311(line *Line) { if nk := conn.st.GetNick(line.Args[1]); nk != nil && !conn.Me().Equals(nk) { conn.st.NickInfo(line.Args[1], line.Args[2], line.Args[3], line.Args[5]) } else { logging.Warn("irc.311(): received WHOIS info for unknown nick %s", line.Args[1]) } }
// 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 _, ok := st.nicks[n]; ok { logging.Warn("Tracker.NewNick(): %s already tracked.", n) return nil } st.nicks[n] = NewNick(n) return st.nicks[n] }
// 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 _, ok := st.chans[c]; ok { logging.Warn("Tracker.NewChannel(): %s already tracked.", c) return nil } st.chans[c] = NewChannel(c) return st.chans[c] }
// Associates a Nick with a Channel func (ch *Channel) addNick(nk *Nick, cp *ChanPrivs) { if _, ok := ch.nicks[nk]; !ok { ch.nicks[nk] = cp ch.lookup[nk.Nick] = nk } else { logging.Warn("Channel.addNick(): %s already on %s.", nk.Nick, ch.Name) } }
// Disassociates a Nick from a Channel. func (ch *Channel) delNick(nk *Nick) { if _, ok := ch.nicks[nk]; ok { delete(ch.nicks, nk) delete(ch.lookup, nk.Nick) } else { logging.Warn("Channel.delNick(): %s not on %s.", nk.Nick, ch.Name) } }
// Handle 671 whois reply (nick connected via SSL) func (conn *Conn) h_671(line *Line) { if nk := conn.st.GetNick(line.Args[1]); nk != nil { nk.Modes.SSL = true } else { logging.Warn("irc.671(): received WHOIS SSL info for unknown nick %s", line.Args[1]) } }
// Handle TOPIC changes for channels func (conn *Conn) h_TOPIC(line *Line) { if ch := conn.st.GetChannel(line.Args[0]); ch != nil { ch.Topic = line.Args[1] } else { logging.Warn("irc.TOPIC(): topic change on unknown channel %s", line.Args[0]) } }
// Disassociates a Channel from a Nick. func (nk *nick) delChannel(ch *channel) { if _, ok := nk.chans[ch]; ok { delete(nk.chans, ch) delete(nk.lookup, ch.name) } else { logging.Warn("Nick.delChannel(): %s not on %s.", nk.nick, ch.name) } }
// Associates a Channel with a Nick. func (nk *nick) addChannel(ch *channel, cp *ChanPrivs) { if _, ok := nk.chans[ch]; !ok { nk.chans[ch] = cp nk.lookup[ch.name] = ch } else { logging.Warn("Nick.addChannel(): %s already on %s.", nk.nick, ch.name) } }
// Handle MODE changes for channels we know about (and our nick personally) func (conn *Conn) h_MODE(line *Line) { if ch := conn.st.GetChannel(line.Args[0]); ch != nil { // channel modes first ch.ParseModes(line.Args[1], line.Args[2:]...) } else if nk := conn.st.GetNick(line.Args[0]); nk != nil { // nick mode change, should be us if nk != conn.cfg.Me { logging.Warn("irc.MODE(): recieved MODE %s for (non-me) nick %s", line.Args[1], line.Args[0]) return } nk.ParseModes(line.Args[1]) } else { logging.Warn("irc.MODE(): not sure what to do with MODE %s", strings.Join(line.Args, " ")) } }
func (line *Line) argslen(minlen int) bool { pc, _, _, _ := runtime.Caller(1) fn := runtime.FuncForPC(pc) if len(line.Args) <= minlen { logging.Warn("%s: too few arguments: %s", fn.Name(), strings.Join(line.Args, " ")) return false } return true }
// Creates a new plugin instance. func New(b bot.Bot) *Plugin { plugin := &Plugin{ bot: b, } // Handle INVITE b.HandleFunc("invite", func(conn *client.Conn, line *client.Line) { if len(line.Args) < 2 { return } channel := line.Args[1] joinChan := make(chan interface{}) go func() { defer conn.HandleFunc("join", func(conn *client.Conn, line *client.Line) { // Is this us joining somewhere? if line.Nick != conn.Me().Nick { return } // JOIN message should always have a channel sent with it if len(line.Args) < 1 { return } // Is this the channel we got invited to? if line.Args[0] != channel { return } // Yup, we're done here joinChan <- struct{}{} }).Remove() select { case <-time.After(10 * time.Second): // Oops, we timed out logging.Warn("Timed out while waiting for us to join %v", channel) return case <-joinChan: } // We have joined successfully, let's send our hello message! b.Privmsg(channel, "Hi, I'm vpn, I automatically get rid of bad "+ "IP-changing ban evading bots! I need half-op (+h/%) to do "+ "this properly, thank you!") }() // Join and wait until joined b.Join(channel) }) return plugin }
// Handle 311 whois reply func (conn *Conn) h_311(line *Line) { if nk := conn.st.GetNick(line.Args[1]); nk != nil && nk != conn.Me() { nk.Ident = line.Args[2] nk.Host = line.Args[3] nk.Name = line.Args[5] } else { logging.Warn("irc.311(): received WHOIS info for unknown nick %s", line.Args[1]) } }