Example #1
0
func (plugin *Plugin) unbanGlobal(hostmask string) {
	modesNative, ok := plugin.isupport.Supports().Modes()
	if !ok {
		modesNative = 1
	}
	modeBuf := util.NewModeChangeBuffer(plugin.bot.Conn(), modesNative)

	for _, channel := range plugin.bot.Channels() {
		// -b will get sent back by the server and then picked up by our mode
		// changing listener that we hooked in the New() method.
		modeBuf.Mode(channel, "-b", hostmask)
	}

	modeBuf.Flush()
}
Example #2
0
// Sets multiple bans in a target channel.
//
// Returns an error slice of the same amount of elements as bans
// passed in, each error corresponding to the respective ban by index.
//
// All errors will be ErrNotAChannel if the target is not a valid channel.
func (p *Plugin) Ban(target string, bans ...TemporaryBan) (retval []error) {
	retval = make([]error, len(bans))

	if ok, _, _ := p.isupport.IsChannel(target); !ok {
		for index, _ := range retval {
			retval[index] = ErrNotAChannel
		}
		return // not a channel
	}

	modesNative, ok := p.isupport.Supports().Modes()
	if !ok {
		modesNative = 1
	}
	modeBuf := util.NewModeChangeBuffer(p.bot.Conn(), modesNative)

	// NOTE - We assume that responses to bans get returned in the same order
	// as we send out the ban requests... this may very well go wrong!

	index := 0
	banSetChans := []chan error{}
	banSetMutex := &sync.Mutex{}

	// Populate banSetChans with channels to send to
	// TODO - There probably is a more efficient way to do this
	for _, _ = range bans {
		banSetChans = append(banSetChans, make(chan error))
	}

	// TODO - This will very well fail on multiple async calls to this method
	// on the same target while both are waiting for replies as the index will
	// get bigger than the length of bans.
	// We also can not use a map by hostmask as some of these replies are far
	// too vague and don't provide anything but our nickname and the error
	// message.
	// Sometimes I really hate the IRC protocol because of things like that...

	defer p.bot.HandleFunc("482", // ERR_CHANOPRIVSNEEDED
		func(conn *client.Conn, line *client.Line) {
			if line.Args[0] != conn.Me().Nick ||
				line.Args[1] != target {
				return
			}

			banSetMutex.Lock()
			defer banSetMutex.Unlock()
			for i := index; i < int(math.Min(float64(len(banSetChans)), float64(uint64(index)+modesNative))); i++ {
				banSetChans[index] <- errors.New("Missing channel operator privileges")
			}

			// This error message counts for all bans sent in the same request
			// line.
			index += int(modesNative)
		}).Remove()
	defer p.bot.HandleFunc("478", // ERR_BANLISTFULL
		func(conn *client.Conn, line *client.Line) {
			if line.Args[0] != conn.Me().Nick ||
				line.Args[1] != target {
				return
			}

			banSetMutex.Lock()
			defer banSetMutex.Unlock()
			for i := index; i < int(math.Min(float64(len(banSetChans)), float64(uint64(index)+modesNative))); i++ {
				banSetChans[index] <- errors.New("The ban list is full")
			}

			// This error message counts for all bans sent in the same request
			// line.
			index += int(modesNative)
		}).Remove()
	defer p.mode.HandleFunc("+b",
		func(e *mode.ModeChangeEvent) {
			if !strings.EqualFold(e.Target, target) {
				return
			}

			banSetMutex.Lock()
			defer banSetMutex.Unlock()

			banSetChans[index] <- nil

			index++
		}).Remove()

	// -b+b will definitely trigger a MODE +b response if the ban can be set
	for _, ban := range bans {
		modeBuf.Mode(target, "-b", ban.Hostmask)
		modeBuf.Mode(target, "+b", ban.Hostmask)
	}
	modeBuf.Flush()

	// Now wait for the responses
	anyBanAppliedCorrectly := false
	for i, ch := range banSetChans {
		select {
		case err := <-ch:
			close(ch)
			retval[i] = err
			if err == nil {
				p.ensureTemporaryBanManager(target).Add(bans[i])
				anyBanAppliedCorrectly = true
			}
		}
	}

	if anyBanAppliedCorrectly {
		p.syncBans(target)
	}

	return
}