2011-10-12 21:59:52 +00:00
|
|
|
package state
|
|
|
|
|
|
|
|
import (
|
2013-09-27 21:19:40 +00:00
|
|
|
"github.com/fluffle/goirc/logging"
|
2014-05-15 11:23:05 +00:00
|
|
|
|
2011-10-12 21:59:52 +00:00
|
|
|
"reflect"
|
|
|
|
)
|
|
|
|
|
2014-12-31 13:17:28 +00:00
|
|
|
// A Nick is returned from the state tracker and contains
|
|
|
|
// a copy of the nick state at a particular time.
|
2011-10-12 21:59:52 +00:00
|
|
|
type Nick struct {
|
|
|
|
Nick, Ident, Host, Name string
|
|
|
|
Modes *NickMode
|
2014-12-31 13:17:28 +00:00
|
|
|
Channels map[string]*ChanPrivs
|
|
|
|
}
|
|
|
|
|
|
|
|
// Internal bookkeeping struct for nicks.
|
|
|
|
type nick struct {
|
|
|
|
nick, ident, host, name string
|
|
|
|
modes *NickMode
|
|
|
|
lookup map[string]*channel
|
|
|
|
chans map[*channel]*ChanPrivs
|
2011-10-12 21:59:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// A struct representing the modes of an IRC Nick (User Modes)
|
|
|
|
// (again, only the ones we care about)
|
|
|
|
//
|
|
|
|
// This is only really useful for me, as we can't see other people's modes
|
|
|
|
// without IRC operator privileges (and even then only on some IRCd's).
|
|
|
|
type NickMode struct {
|
2013-01-06 18:52:11 +00:00
|
|
|
// MODE +B, +i, +o, +w, +x, +z
|
|
|
|
Bot, Invisible, Oper, WallOps, HiddenHost, SSL bool
|
2011-10-12 21:59:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Map *irc.NickMode fields to IRC mode characters and vice versa
|
|
|
|
var StringToNickMode = map[string]string{}
|
|
|
|
var NickModeToString = map[string]string{
|
2013-01-06 18:52:11 +00:00
|
|
|
"Bot": "B",
|
2011-10-12 21:59:52 +00:00
|
|
|
"Invisible": "i",
|
|
|
|
"Oper": "o",
|
|
|
|
"WallOps": "w",
|
|
|
|
"HiddenHost": "x",
|
|
|
|
"SSL": "z",
|
|
|
|
}
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
for k, v := range NickModeToString {
|
|
|
|
StringToNickMode[v] = k
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************\
|
2014-12-31 13:17:28 +00:00
|
|
|
* nick methods for state management
|
2011-10-12 21:59:52 +00:00
|
|
|
\******************************************************************************/
|
|
|
|
|
2014-12-31 13:17:28 +00:00
|
|
|
func newNick(n string) *nick {
|
|
|
|
return &nick{
|
|
|
|
nick: n,
|
|
|
|
modes: new(NickMode),
|
|
|
|
chans: make(map[*channel]*ChanPrivs),
|
|
|
|
lookup: make(map[string]*channel),
|
2011-10-12 21:59:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-31 13:17:28 +00:00
|
|
|
// Returns a copy of the internal tracker nick state at this time.
|
|
|
|
// Relies on tracker-level locking for concurrent access.
|
|
|
|
func (nk *nick) Nick() *Nick {
|
|
|
|
n := &Nick{
|
|
|
|
Nick: nk.nick,
|
|
|
|
Ident: nk.ident,
|
|
|
|
Host: nk.host,
|
|
|
|
Name: nk.name,
|
|
|
|
Modes: nk.modes.Copy(),
|
|
|
|
Channels: make(map[string]*ChanPrivs),
|
|
|
|
}
|
|
|
|
for c, cp := range nk.chans {
|
|
|
|
n.Channels[c.name] = cp.Copy()
|
|
|
|
}
|
|
|
|
return n
|
2011-10-12 21:59:52 +00:00
|
|
|
}
|
|
|
|
|
2014-12-31 13:17:28 +00:00
|
|
|
func (nk *nick) isOn(ch *channel) (*ChanPrivs, bool) {
|
|
|
|
cp, ok := nk.chans[ch]
|
|
|
|
return cp.Copy(), ok
|
2011-10-12 21:59:52 +00:00
|
|
|
}
|
|
|
|
|
2011-10-19 23:10:33 +00:00
|
|
|
// Associates a Channel with a Nick.
|
2014-12-31 13:17:28 +00:00
|
|
|
func (nk *nick) addChannel(ch *channel, cp *ChanPrivs) {
|
2011-10-19 23:10:33 +00:00
|
|
|
if _, ok := nk.chans[ch]; !ok {
|
|
|
|
nk.chans[ch] = cp
|
2014-12-31 13:17:28 +00:00
|
|
|
nk.lookup[ch.name] = ch
|
2011-10-22 22:57:22 +00:00
|
|
|
} else {
|
2014-12-31 13:17:28 +00:00
|
|
|
logging.Warn("Nick.addChannel(): %s already on %s.", nk.nick, ch.name)
|
2011-10-12 21:59:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-19 23:10:33 +00:00
|
|
|
// Disassociates a Channel from a Nick.
|
2014-12-31 13:17:28 +00:00
|
|
|
func (nk *nick) delChannel(ch *channel) {
|
2011-10-19 23:10:33 +00:00
|
|
|
if _, ok := nk.chans[ch]; ok {
|
2011-11-13 13:32:53 +00:00
|
|
|
delete(nk.chans, ch)
|
2014-12-31 13:17:28 +00:00
|
|
|
delete(nk.lookup, ch.name)
|
2011-10-22 22:57:22 +00:00
|
|
|
} else {
|
2014-12-31 13:17:28 +00:00
|
|
|
logging.Warn("Nick.delChannel(): %s not on %s.", nk.nick, ch.name)
|
2011-10-12 21:59:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse mode strings for a Nick.
|
2014-12-31 13:17:28 +00:00
|
|
|
func (nk *nick) parseModes(modes string) {
|
2011-10-12 21:59:52 +00:00
|
|
|
var modeop bool // true => add mode, false => remove mode
|
|
|
|
for i := 0; i < len(modes); i++ {
|
|
|
|
switch m := modes[i]; m {
|
|
|
|
case '+':
|
|
|
|
modeop = true
|
|
|
|
case '-':
|
|
|
|
modeop = false
|
2013-01-06 18:52:11 +00:00
|
|
|
case 'B':
|
2014-12-31 13:17:28 +00:00
|
|
|
nk.modes.Bot = modeop
|
2011-10-12 21:59:52 +00:00
|
|
|
case 'i':
|
2014-12-31 13:17:28 +00:00
|
|
|
nk.modes.Invisible = modeop
|
2011-10-12 21:59:52 +00:00
|
|
|
case 'o':
|
2014-12-31 13:17:28 +00:00
|
|
|
nk.modes.Oper = modeop
|
2011-10-12 21:59:52 +00:00
|
|
|
case 'w':
|
2014-12-31 13:17:28 +00:00
|
|
|
nk.modes.WallOps = modeop
|
2011-10-12 21:59:52 +00:00
|
|
|
case 'x':
|
2014-12-31 13:17:28 +00:00
|
|
|
nk.modes.HiddenHost = modeop
|
2011-10-12 21:59:52 +00:00
|
|
|
case 'z':
|
2014-12-31 13:17:28 +00:00
|
|
|
nk.modes.SSL = modeop
|
2011-10-19 23:10:33 +00:00
|
|
|
default:
|
2013-01-23 22:33:01 +00:00
|
|
|
logging.Info("Nick.ParseModes(): unknown mode char %c", m)
|
2011-10-12 21:59:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-31 13:17:28 +00:00
|
|
|
// Returns true if the Nick is associated with the Channel.
|
|
|
|
func (nk *Nick) IsOn(ch string) (*ChanPrivs, bool) {
|
|
|
|
cp, ok := nk.Channels[ch]
|
|
|
|
return cp, ok
|
|
|
|
}
|
2014-05-15 11:23:05 +00:00
|
|
|
|
2014-12-31 13:17:28 +00:00
|
|
|
// Tests Nick equality.
|
|
|
|
func (nk *Nick) Equals(other *Nick) bool {
|
|
|
|
return reflect.DeepEqual(nk, other)
|
|
|
|
}
|
2014-05-15 11:23:05 +00:00
|
|
|
|
2014-12-31 13:17:28 +00:00
|
|
|
// Duplicates a NickMode struct.
|
|
|
|
func (nm *NickMode) Copy() *NickMode {
|
2018-09-06 21:15:14 +00:00
|
|
|
if nm == nil {
|
|
|
|
return nil
|
|
|
|
}
|
2014-12-31 13:17:28 +00:00
|
|
|
n := *nm
|
|
|
|
return &n
|
2013-01-06 19:13:06 +00:00
|
|
|
}
|
|
|
|
|
2014-12-31 13:17:28 +00:00
|
|
|
// Tests NickMode equality.
|
|
|
|
func (nm *NickMode) Equals(other *NickMode) bool {
|
|
|
|
return reflect.DeepEqual(nm, other)
|
2013-01-06 19:13:06 +00:00
|
|
|
}
|
|
|
|
|
2011-10-12 21:59:52 +00:00
|
|
|
// Returns a string representing the nick. Looks like:
|
|
|
|
// Nick: <nick name> e.g. CowMaster
|
|
|
|
// Hostmask: <ident@host> e.g. moo@cows.org
|
|
|
|
// Real Name: <real name> e.g. Steve "CowMaster" Bush
|
|
|
|
// Modes: <nick modes> e.g. +z
|
|
|
|
// Channels:
|
|
|
|
// <channel>: <privs> e.g. #moo: +o
|
|
|
|
// ...
|
2011-10-19 23:10:33 +00:00
|
|
|
func (nk *Nick) String() string {
|
|
|
|
str := "Nick: " + nk.Nick + "\n\t"
|
|
|
|
str += "Hostmask: " + nk.Ident + "@" + nk.Host + "\n\t"
|
|
|
|
str += "Real Name: " + nk.Name + "\n\t"
|
|
|
|
str += "Modes: " + nk.Modes.String() + "\n\t"
|
2011-10-12 21:59:52 +00:00
|
|
|
str += "Channels: \n"
|
2014-12-31 13:17:28 +00:00
|
|
|
for ch, cp := range nk.Channels {
|
|
|
|
str += "\t\t" + ch + ": " + cp.String() + "\n"
|
2011-10-12 21:59:52 +00:00
|
|
|
}
|
|
|
|
return str
|
|
|
|
}
|
|
|
|
|
2014-12-31 13:17:28 +00:00
|
|
|
func (nk *nick) String() string {
|
|
|
|
return nk.Nick().String()
|
|
|
|
}
|
|
|
|
|
2011-10-12 21:59:52 +00:00
|
|
|
// Returns a string representing the nick modes. Looks like:
|
|
|
|
// +iwx
|
|
|
|
func (nm *NickMode) String() string {
|
2018-09-06 21:23:59 +00:00
|
|
|
if nm == nil {
|
|
|
|
return "No modes set"
|
|
|
|
}
|
2011-10-12 21:59:52 +00:00
|
|
|
str := "+"
|
|
|
|
v := reflect.Indirect(reflect.ValueOf(nm))
|
|
|
|
t := v.Type()
|
|
|
|
for i := 0; i < v.NumField(); i++ {
|
|
|
|
switch f := v.Field(i); f.Kind() {
|
|
|
|
// only bools here at the mo!
|
|
|
|
case reflect.Bool:
|
|
|
|
if f.Bool() {
|
|
|
|
str += NickModeToString[t.Field(i).Name]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if str == "+" {
|
|
|
|
str = "No modes set"
|
|
|
|
}
|
|
|
|
return str
|
|
|
|
}
|