Ejemplo n.º 1
0
// 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()
}
Ejemplo n.º 2
0
// 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)
		}
	}
}
Ejemplo n.º 3
0
// 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
}
Ejemplo n.º 4
0
// 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)
		}
	}
}
Ejemplo n.º 5
0
func (hs *hSet) remove(hn *hNode) {
	hs.Lock()
	defer hs.Unlock()
	l, ok := hs.set[hn.event]
	if !ok {
		logging.Error("Removing node for unknown event '%s'", hn.event)
		return
	}
	if hn.next == nil {
		l.end = hn.prev
	} else {
		hn.next.prev = hn.prev
	}
	if hn.prev == nil {
		l.start = hn.next
	} else {
		hn.prev.next = hn.next
	}
	hn.next = nil
	hn.prev = nil
	hn.set = nil
	if l.start == nil || l.end == nil {
		delete(hs.set, hn.event)
	}
}
Ejemplo n.º 6
0
func pushAuthHTTP(rw http.ResponseWriter, req *http.Request) {
	if err := req.ParseForm(); err != nil {
		http.Redirect(rw, req, pushFailureURL("parse"), 302)
		return
	}
	if req.FormValue("error") != "" {
		http.Redirect(rw, req, pushFailureURL("denied"), 302)
		return
	}
	id := req.FormValue("state")
	s := pc.GetByB64(id)
	if id == "" || s == nil {
		http.Redirect(rw, req, pushFailureURL("nostate"), 302)
		return
	}
	code := req.FormValue("code")
	if code == "" {
		http.Redirect(rw, req, pushFailureURL("notoken"), 302)
		return
	}
	tok, err := push.Exchange(code)
	if err != nil {
		logging.Error("Failed to get access token for %s: %v", s.Nick, err)
		http.Redirect(rw, req, pushFailureURL("exchange"), 302)
		return
	}

	s.Token = tok
	pc.SetState(s)
	http.Redirect(rw, req, pushDeviceURL(id), 302)
}
Ejemplo n.º 7
0
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)
	}
}
Ejemplo n.º 8
0
func (ns *namespace) get(key string) interface{} {
	var e Entry
	if err := ns.Get(ns.K(key), &e); err != nil && err != mgo.ErrNotFound && err != boltdb.ErrTxNotWritable {
		logging.Error("Couldn't get config entry for ns=%q key=%q: %v", ns.ns, key, err)
		return nil
	}
	return e.Value
}
Ejemplo n.º 9
0
func (st *stateTracker) delNick(nk *Nick) {
	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)
		}
	}
}
Ejemplo n.º 10
0
func Migrate() error {
	ms.Lock()
	defer ms.Unlock()
	ms.db.Init(Bolt, COLLECTION, nil)

	failed := []string{}
	for coll, m := range ms.migrators {
		if m.migrated {
			continue
		}
		logging.Debug("Migrating %q.", coll)
		if err := m.Migrate(); err != nil {
			logging.Error("Migrating %q failed: %v", coll, err)
			failed = append(failed, coll)
			continue
		}
		if differ, ok := m.Migrator.(Differ); ok {
			before, after, err := differ.Diff()
			if err != nil {
				logging.Error("Diffing %q failed: %v", coll, err)
				failed = append(failed, coll)
				continue
			}
			sort.Strings(before)
			sort.Strings(after)
			unified, err := diff.Unified(before, after)
			if err != nil {
				logging.Error("Migration diff: %v\n%s", err, strings.Join(unified, "\n"))
				failed = append(failed, coll)
				continue
			}
		}
		if err := ms.db.Put(K{{"collection", coll}}, &done{true}); err != nil {
			logging.Warn("Setting migrated status for %q: %v", coll, err)
		}
		m.migrated = true
	}
	if len(failed) > 0 {
		return fmt.Errorf("migration failed for: \"%s\"",
			strings.Join(failed, "\", \""))
	}
	return nil
}
Ejemplo n.º 11
0
func pushDeviceHTTP(rw http.ResponseWriter, req *http.Request) {
	if err := req.ParseForm(); err != nil {
		http.Redirect(rw, req, pushFailureURL("parse"), 302)
		return
	}
	id := req.FormValue("state")
	s := pc.GetByB64(id)
	if id == "" || s == nil {
		http.Redirect(rw, req, pushFailureURL("nostate"), 302)
		return
	}
	if req.Method == "POST" {
		if s.Iden = req.FormValue("iden"); s.Iden == "" {
			http.Redirect(rw, req, pushFailureURL("noiden"), 302)
			return
		}
		s.Pin = fmt.Sprintf("%06x", rand.Intn(1e6))
		if err := push.Confirm(s); err != nil {
			logging.Error("Failed to send confirmation push for %s: %v", s.Nick, err)
			http.Redirect(rw, req, pushFailureURL("push"), 302)
			return
		}
		pc.SetState(s)
		http.Redirect(rw, req, pushSuccessURL(), 302)
		return
	}
	// get device list and print a form
	devs, err := push.GetDevices(s)
	if err != nil {
		logging.Error("Failed to get devices for %s: %v", s.Nick, err)
		http.Redirect(rw, req, pushFailureURL("device"), 302)
		return
	}
	if len(devs) == 0 {
		strings.NewReader(pushNoDeviceHTML).WriteTo(rw)
		return
	}
	if err = pushDeviceTmpl.Execute(rw, &pushDevice{id, devs}); err != nil {
		logging.Error("Template execution failed: %v", err)
		// assuming here that failure occurred because we couldn't write
		return
	}
}
Ejemplo n.º 12
0
func Init() *Collection {
	pc := &Collection{db.Init().C(COLLECTION)}
	if err := pc.EnsureIndex(mgo.Index{
		Key:    []string{"nick"},
		Unique: true,
	}); err != nil {
		logging.Error("Couldn't create an index on push: %s", err)
	}
	return pc
}
Ejemplo n.º 13
0
// Write a \r\n terminated line of output to the connected server,
// using Hybrid's algorithm to rate limit if conn.cfg.Flood is false.
func (conn *Conn) write(line string) {
	if !conn.cfg.Flood {
		if t := conn.rateLimit(len(line)); t != 0 {
			// sleep for the current line's time value before sending it
			logging.Info("irc.rateLimit(): Flood! Sleeping for %.2f secs.",
				t.Seconds())
			<-time.After(t)
		}
	}

	if _, err := conn.io.WriteString(line + "\r\n"); err != nil {
		logging.Error("irc.send(): %s", err.Error())
		conn.shutdown()
		return
	}
	if err := conn.io.Flush(); err != nil {
		logging.Error("irc.send(): %s", err.Error())
		conn.shutdown()
		return
	}
	logging.Debug("-> %s", line)
}
Ejemplo n.º 14
0
func (plugin *Plugin) isAdmin(mask string) bool {
	for _, adminmask := range plugin.Admins {
		// TODO - Test this implementation
		adminmask = regexp.QuoteMeta(adminmask)
		adminmask = strings.Replace(adminmask, "\\*", ".*", -1)
		adminmask = strings.Replace(adminmask, "\\?", ".?", -1)
		if matched, err := regexp.MatchString(adminmask, mask); matched {
			return true
		} else if err != nil {
			logging.Error("vpnbot.Plugin: isAdmin regular expression failed: %v",
				err)
			break
		}
	}

	return false
}
Ejemplo n.º 15
0
// send is started as a goroutine after a connection is established.
// It shuttles data from the output channel to write(), and is killed
// when Conn.die is closed.
func (conn *Conn) send() {
	for {
		select {
		case line := <-conn.out:
			if err := conn.write(line); err != nil {
				logging.Error("irc.send(): %s", err.Error())
				// We can't defer this, because shutdown() waits for it.
				conn.wg.Done()
				conn.shutdown()
				return
			}
		case <-conn.die:
			// control channel closed, bail out
			conn.wg.Done()
			return
		}
	}
}
Ejemplo n.º 16
0
func Client(cfg *Config) *Conn {
	if cfg == nil {
		cfg = NewConfig("__idiot__")
	}
	if cfg.Me == nil || cfg.Me.Nick == "" || cfg.Me.Ident == "" {
		cfg.Me = state.NewNick("__idiot__")
		cfg.Me.Ident = "goirc"
		cfg.Me.Name = "Powered by GoIRC"
	}

	dialer := new(net.Dialer)
	if cfg.LocalAddr != "" {
		if !hasPort(cfg.LocalAddr) {
			cfg.LocalAddr += ":0"
		}

		local, err := net.ResolveTCPAddr("tcp", cfg.LocalAddr)
		if err == nil {
			dialer.LocalAddr = local
		} else {
			logging.Error("irc.Client(): Cannot resolve local address %s: %s", cfg.LocalAddr, err)
		}
	}

	conn := &Conn{
		cfg:         cfg,
		dialer:      dialer,
		in:          make(chan *Line, 32),
		out:         make(chan string, 32),
		intHandlers: handlerSet(),
		fgHandlers:  handlerSet(),
		bgHandlers:  handlerSet(),
		stRemovers:  make([]Remover, 0, len(stHandlers)),
		lastsent:    time.Now(),
	}
	conn.addIntHandlers()
	conn.initialise()
	return conn
}
Ejemplo n.º 17
0
// receive one \r\n terminated line from peer, parse and dispatch it
func (conn *Conn) recv() {
	for {
		s, err := conn.io.ReadString('\n')
		if err != nil {
			if err != io.EOF {
				logging.Error("irc.recv(): %s", err.Error())
			}
			// We can't defer this, because shutdown() waits for it.
			conn.wg.Done()
			conn.shutdown()
			return
		}
		s = strings.Trim(s, "\r\n")
		logging.Debug("<- %s", s)

		if line := ParseLine(s); line != nil {
			line.Time = time.Now()
			conn.in <- line
		} else {
			logging.Warn("irc.recv(): problems parsing line:\n  %s", s)
		}
	}
}
Ejemplo n.º 18
0
func mongoIndexes(c db.Collection) {
	err := c.Mongo().EnsureIndex(mgo.Index{Key: []string{"ns", "key"}, Unique: true})
	if err != nil {
		logging.Error("Couldn't create index on sp0rkle.conf: %s", err)
	}
}
Ejemplo n.º 19
0
func (conn *Conn) LogPanic(line *Line) {
	if err := recover(); err != nil {
		_, f, l, _ := runtime.Caller(2)
		logging.Error("%s:%d: panic: %v", f, l, err)
	}
}
Ejemplo n.º 20
0
// The main program logic.
func main() {
	// Load configuration path from flags
	flag.Parse()

	// Initialize the logger
	logger = glogging.GLogger{}
	logging.SetLogger(logger)

	// Check if we're supposed to generate a default config
	if *generateDefault {
		logger.Debug("Saving default configuration...")
		if err := defaultConfiguration.Save(*configPath); err != nil {
			logger.Error("Failed at saving default configuration: %v", err)
			os.Exit(1)
		}
		logger.Info("Saved default configuration.")
		os.Exit(0)
	}

	// Check if we're supposed to migrate an old config
	if *migratePath != "" {
		logger.Debug("Migrating old configuration...")
		if c, err := LoadV1Config(*migratePath); err != nil {
			logger.Error("Failed to load old configuration: %v", err)
			os.Exit(1)
		} else {
			newC := c.Migrate()
			if err := newC.Save(*configPath); err != nil {
				logger.Error("Migration failed: %v", err)
				os.Exit(1)
			}
			if err := newC.Validate(); err != nil {
				logger.Warn("Migration successful but found errors while "+
					"validating the new configuration, you should fix this before "+
					"running the bot: %v", err)
				os.Exit(2)
			}
		}
		logger.Info("Migration successful.")
		os.Exit(0)
	}

	// Load configuration from configuration path
	if c, err := Load(*configPath); err != nil {
		logger.Error("Can't load configuration from %v: %v\n", *configPath, err)
		os.Exit(1)
	} else {
		loadedConfiguration = c
	}

	logger.Debug("Loaded configuration will be printed below.")
	logger.Debug("%#v", loadedConfiguration)

	// Validate configuration
	if err := loadedConfiguration.Validate(); err != nil {
		logger.Error("The configuration is invalid: %v\n", err)
		os.Exit(2)
	}

	// Now initialize the bot
	logger.Info("Initializing vpnbot %v...", version)
	b := bot.NewBot(
		loadedConfiguration.Server.Address,
		loadedConfiguration.Server.SSL,
		loadedConfiguration.Nick,
		loadedConfiguration.Ident,
		[]string{})
	b.Conn().Config().Version = fmt.Sprintf("vpnbot/%v", version)
	b.Conn().Config().Recover = func(conn *client.Conn, line *client.Line) {
		if err := recover(); err != nil {
			logging.Error("An internal error occurred: %v\n%v",
				err, string(debug.Stack()))
		}
	}
	b.Conn().Config().Pass = loadedConfiguration.Server.Password
	if loadedConfiguration.Name != "" {
		b.Conn().Config().Me.Name = loadedConfiguration.Name
	}

	// Load plugins
	// TODO - Move this into its own little intelligent loader struct, maybe.
	isupportPlugin := isupport.Register(b)
	modePlugin := mode.Register(b, isupportPlugin)
	nickservPlugin := nickserv.Register(b, modePlugin)
	nickservPlugin.Username = loadedConfiguration.NickServ.Username
	nickservPlugin.Password = loadedConfiguration.NickServ.Password
	nickservPlugin.Channels = loadedConfiguration.Channels

	switch {
	case *makeTempBans: // Run in tempban dumping mode
		// Prepare channels to let us know about dumped bans
		doneChan := make(map[string]chan interface{})
		for _, channel := range loadedConfiguration.Channels {
			doneChan[strings.ToLower(channel)] = make(chan interface{}, 1)
		}

		// Load the tempban dumping plugin
		dumptempbanPlugin := dumptempban.Register(b, isupportPlugin, modePlugin)
		dumptempbanPlugin.DumpedBansFunc = func(target string, num int, err error) {
			if err != nil {
				logging.Error("Failed to dump bans for %v: %v", target, err)
			} else {
				logging.Info("Dumped %v bans for %v successfully.", num, target)
			}
			if done, ok := doneChan[strings.ToLower(target)]; ok {
				done <- nil
			}
		}

		// Start up the bot asynchronously
		go b.Run()

		// Wait for all channels to be done
		for _, done := range doneChan {
			<-done
		}
		b.Quit("Ban dumping done.")

	default: // Run normally
		// Load plugins
		autojoin.Register(b)
		adminplugin.Register(b, loadedConfiguration.Admins)
		bots.Register(b, isupportPlugin)
		tempbanPlugin := tempban.Register(b, isupportPlugin, modePlugin)
		tempbanPlugin.OldHostmasks = loadedConfiguration.OldHostmasks
		whoisPlugin := whois.Register(b, isupportPlugin)
		vpnbotPlugin := vpnbot.Register(b, whoisPlugin, isupportPlugin,
			tempbanPlugin, nickservPlugin)
		vpnbotPlugin.Admins = loadedConfiguration.Admins

		// This is to update the configuration when the bot joins channels
		b.HandleFunc("join",
			func(c *client.Conn, line *client.Line) {
				// Arguments: [ <channel> ]

				// Make sure this is about us
				if line.Nick != c.Me().Nick {
					return
				}

				// I don't think method calls are a good idea in a loop
				channel := line.Target()

				// See if we already had this channel saved
				for _, savedChannel := range loadedConfiguration.Channels {
					if strings.EqualFold(savedChannel, channel) {
						return // Channel already saved
					}
				}

				// Store this channel
				logger.Info("Adding %v to configured channels", channel)
				loadedConfiguration.Channels = append(
					loadedConfiguration.Channels, channel)

				// And save to configuration file!
				loadedConfiguration.Save(*configPath)
			})
		b.HandleFunc("kick",
			func(c *client.Conn, line *client.Line) {
				// Arguments: [ <channel>, <nick>, <reason> ]

				// Make sure this is about us
				if line.Args[1] != c.Me().Nick {
					return
				}

				// I don't think method calls are a good idea in a loop
				channel := line.Target()

				for index, savedChannel := range loadedConfiguration.Channels {
					if strings.EqualFold(savedChannel, channel) {
						// Delete the channel
						logger.Info("Removing %v from configured channels", savedChannel)
						loadedConfiguration.Channels = append(
							loadedConfiguration.Channels[0:index],
							loadedConfiguration.Channels[index+1:]...)

						// And save to configuration file!
						loadedConfiguration.Save(*configPath)
						return
					}
				}
			})
		b.HandleFunc("part",
			func(c *client.Conn, line *client.Line) {
				// Arguments: [ <channel> (, <reason>) ]

				// Make sure this is about us
				if line.Nick != c.Me().Nick {
					return
				}

				// I don't think method calls are a good idea in a loop
				channel := line.Target()

				for index, savedChannel := range loadedConfiguration.Channels {
					if strings.EqualFold(savedChannel, channel) {
						// Delete the channel
						logger.Info("Removing %v from configured channels", savedChannel)
						loadedConfiguration.Channels = append(
							loadedConfiguration.Channels[0:index],
							loadedConfiguration.Channels[index+1:]...)

						// And save to configuration file!
						loadedConfiguration.Save(*configPath)
						return
					}
				}
			})

		// Run the bot
		b.Run()
	}
}
Ejemplo n.º 21
0
func (ns *namespace) set(key string, value interface{}) {
	e := Entry{Ns: ns.ns, Key: key, Value: value}
	if err := ns.Put(ns.K(key), &e); err != nil {
		logging.Error("Couldn't set config entry %q: %v", e, err)
	}
}