// HasOpFlag returns whether the user has the given op flag. // This both checks for the presence of the flag, and, if the flag is default, // the "on" keyword for default privileges. This function should not be used as // a direct means of determining a user's ability to do something; instead, // the appropriate permission check should be used, as this permits module to // hook the check on other conditions. It should be used IN said permission // hooks. // // If ch is non-nil, this is a channel op flag check and the user's membership // entry on the channel will be checked. If no such entry exists, the check // automatically fails. If ch is nil, this is a server op flag check, and the user's own metadata will be checked. func HasOpFlag(u *core.User, ch *core.Channel, flag string) bool { var e core.Extensible var defwords []string if ch == nil { e = u defwords = defServerOp } else { if m := ch.GetMember(u); m != nil { e = m } else { return false } defwords = defChanOp } words := strings.Fields(e.Data("op")) for _, word := range words { if word == flag { return true } if word == "on" { for _, defword := range defwords { if defword == flag { return true } } } } return false }
// Voiced users overrride most restrictions on speaking. func voiceOverride(_ string, source *core.User, target *core.Channel, msg []byte) (int, os.Error) { if m := target.GetMember(source); m != nil { if m.Data("voiced") != "" { return 100, nil } } return 0, nil }
// If the target is already in a channel, you can't invite them. // This is deliberately weaker than externalInvite, so external users cannot // use invites to see whether a user is in the channel. func stupidInvite(_ string, source, target *core.User, msg []byte) (int, os.Error) { var ch *core.Channel if ch = core.FindChannel("", string(msg)); ch == nil { return -100, os.NewError("No such channel.") } if m := ch.GetMember(target); m != nil { return -99, os.NewError("The target is already in that channel.") } return 0, nil }
// Channel operators are immune to being removed by anything below server op // level aside themselves. They must be deopped first. func opKickImmune(_ string, source, target *core.User, ch *core.Channel) (int, os.Error) { if source == target { return 0, nil } if m := ch.GetMember(target); m != nil { if m.Data("op") != "" { return -1000000, os.NewError("Target user is an operator, and cannot be kicked without being deopped.") } } return 0, nil }
// CheckChanViewPerm returns the full permissions value for CheckChanView. // This is not at present hookable. func CheckChanViewPerm(pkg string, u *core.User, ch *core.Channel) (int, os.Error) { if m := ch.GetMember(u); m != nil { return 100, nil } if ch.Data("hidden") == "" { return 1, nil } return -1, os.NewError("Channel is hidden.") }
func cmdWho(source interface{}, params [][]byte) { c := source.(*Client) channame := string(params[0]) if channame[0] == '#' { channame = channame[1:] } var ch *core.Channel if ch = core.FindChannel("", channame); ch == nil { c.SendLineTo(nil, "403", "#%s :No such channel.", channame) return } // If the user isn't on the channel, don't let them check unless they // can view private channel data. if m := ch.GetMember(c.u); m == nil { if ok, err := perm.CheckChanViewData(me, c.u, ch, "members"); !ok { c.SendLineTo(nil, "482", "#%s :%s", ch.Name(), err) return } } it := ch.Users() c.WriteBlock(func() []byte { if it == nil { return nil } user := it.User() var prefixes string if user.Data("away") == "" { prefixes += "H" } else { prefixes += "G" } if user.Data("op") != "" { prefixes += "*" } prefixes += ChanModes.GetPrefixes(it) servername := core.Global.Data("name") result := fmt.Sprintf(":%s 352 %s #%s %s %s %s %s %s :0 %s\r\n", servername, c.u.Nick(), channame, user.GetIdent(), user.GetHostname(), servername, user.Nick(), prefixes, user.Data("realname")) it = it.ChanNext() return []byte(result) }) c.SendLineTo(nil, "315", "%s :End of /WHO list.", params[0]) }
func cmdNames(source interface{}, params [][]byte) { c := source.(*Client) channame := string(params[0]) if len(channame) > 0 && channame[0] == '#' { channame = channame[1:] } var ch *core.Channel if ch = core.FindChannel("", channame); ch == nil { c.SendLineTo(nil, "403", "#%s :No such channel.", channame) return } // If the user isn't on the channel, don't let them check unless they // can view private channel data. // Otherwise, get their prefixes. var myprefix string if m := ch.GetMember(c.u); m == nil { if ok, err := perm.CheckChanViewData(me, c.u, ch, "members"); !ok { c.SendLineTo(nil, "482", "#%s :%s", ch.Name(), err) return } myprefix = "=" } else { myprefix = ChanModes.GetPrefixes(m) } it := ch.Users() c.WriteBlock(func() []byte { if it == nil { return nil } names := fmt.Sprintf(":%s 353 %s %s #%s :", core.Global.Data("name"), c.u.Nick(), myprefix, channame) for ; it != nil; it = it.ChanNext() { name := ChanModes.GetPrefixes(it) + it.User().Nick() if len(names)+len(name) > 508 { break } names += " " + name } names += "\r\n" return []byte(names) }) c.SendLineTo(nil, "366", "#%s :End of /NAMES list", channame) }
// If a user is not in a channel, they don't get to invite users into it. func externalInvite(_ string, source, target *core.User, msg []byte) (int, os.Error) { if source == nil { return 0, nil } var ch *core.Channel if ch = core.FindChannel("", string(msg)); ch == nil { return -100, os.NewError("No such channel.") } if m := ch.GetMember(source); m == nil { return -100, os.NewError("You are not in that channel.") } return 0, nil }
func cmdOpflags(source interface{}, params [][]byte) { c := source.(*Client) channame := string(params[0]) if channame[0] == '#' { channame = channame[1:] } var ch *core.Channel if ch = core.FindChannel("", channame); ch == nil { c.SendLineTo(nil, "403", "#%s :No such channel.", channame) return } // If the user isn't on the channel, don't let them check unless they // can view private channel data. if m := ch.GetMember(c.u); m == nil { if ok, err := perm.CheckChanViewData(me, c.u, ch, "members"); !ok { c.SendLineTo(nil, "482", "#%s :%s", ch.Name(), err) return } } var target *core.User if target = core.GetUserByNick(string(params[1])); target == nil { c.SendLineTo(nil, "401", "%s :No such user.", params[1]) return } var m *core.Membership if m = ch.GetMember(target); m == nil { c.SendLineTo(nil, "304", ":OPFLAGS #%s: %s is not in the channel.", ch.Name(), target.Nick()) return } if ok, err := perm.CheckMemberViewData(me, c.u, m, "op"); !ok { c.SendLineTo(nil, "482", "#%s :%s: %s", ch.Name(), target.Nick(), err) return } var flags string if flags = m.Data("op"); flags == "" { c.SendLineTo(nil, "304", ":OPFLAGS #%s: %s has no channel op flags.", ch.Name(), target.Nick()) return } if flags == "on" { flags = perm.DefaultChanOp() } c.SendLineTo(nil, "304", ":OPFLAGS #%s: %s has channel op flags: %s", ch.Name(), target.Nick(), flags) }
// If a user is not in the channel, they don't get to message it. func externalMsg(_ string, source *core.User, target *core.Channel, msg []byte) (int, os.Error) { if m := target.GetMember(source); m == nil { return -100, os.NewError("You are not in the channel.") } return 0, nil }
// ParseModeLine parses a line of mode changes into core.DataChange structs. // Redundant changes are compressed down into one. // An error is returned if unknown modes are encountered, or modes are dropped // due to missing parameters. The remainder of the modes are still parsed. // e is the user or channel being changed. func (p *ModeParser) ParseModeLine(source *core.User, e core.Extensible, modeline []byte, params []string) ([]core.DataChange, os.Error) { var adding bool = true var unknown string var missing string var param int modes := string(modeline) changes := make(map[string]core.DataChange) for _, char := range modes { if char == '+' { adding = true continue } else if char == '-' { adding = false continue } if v, ok := p.simple[char]; ok { if v, ok := p.extended[char]; ok { newchanges := v(adding, e, "") for _, it := range newchanges { changes[it.Name] = it } continue } var change core.DataChange change.Name = v if adding { change.Data = "on" } changes[change.Name] = change continue } if v, ok := p.parametered[char]; ok { var change core.DataChange if adding { if param >= len(params) { missing += string(char) continue } if v, ok := p.extended[char]; ok { newch := v(adding, e, params[param]) param++ for _, it := range newch { changes[it.Name] = it } continue } change.Name = v change.Data = params[param] param++ } else { if v, ok := p.extended[char]; ok { newchanges := v(adding, e, "") for _, it := range newchanges { changes[it.Name] = it } continue } change.Name = v } changes[change.Name] = change continue } if v, ok := p.list[char]; ok { if param >= len(params) { missing += string(char) continue } var change core.DataChange cparam := params[param] param++ if v, ok := p.extended[char]; ok { newchanges := v(adding, e, cparam) for _, it := range newchanges { if it.Data != "" { it.Data += fmt.Sprintf(" setby-%s setat-%d", source.GetSetBy(), time.Seconds()) } changes[it.Name] = it } continue } if adding { change.Name = v + " " + cparam change.Data = fmt.Sprintf("on setby-%s setat-%d", source.GetSetBy(), time.Seconds()) } else { change.Name = v + " " + cparam } changes[change.Name] = change continue } if v, ok := p.membership[char]; ok { var ch *core.Channel var ok bool if ch, ok = e.(*core.Channel); !ok { continue } if param >= len(params) { missing += string(char) continue } par := params[param] param++ if v, ok := p.extended[char]; ok { newchanges := v(adding, e, par) for _, it := range newchanges { if it.Member != nil { changes["m"+it.Member.User().ID()+" "+it.Name] = it } else { changes[it.Name] = it } } continue } var u *core.User var m *core.Membership if u = core.GetUser(par); u == nil { if u = core.GetUserByNick(par); u == nil { continue } } if m = ch.GetMember(u); m == nil { continue } var change core.DataChange change.Name = v change.Member = m if adding { change.Data = "on" } changes["m"+change.Member.User().ID()+" "+change.Name] = change continue } unknown += string(char) } // Turn the modes into a slice. c := make([]core.DataChange, 0, len(changes)) for _, change := range changes { c = append(c, change) } // Get the error, if we had one. var errstring string if unknown != "" { errstring += "Unknown mode letters: " + unknown if missing != "" { errstring += " " } } if missing != "" { errstring += "Missing parameters for: " + missing } var err os.Error if errstring != "" { err = os.NewError(errstring) } return c, err }
// Function handling processing of extended op syntax into metadata. // It returns the data change object. func processOp(adding bool, ch *core.Channel, param string) []core.DataChange { var change core.DataChange change.Name = "op" change.Data = perm.DefaultChanOp() // Find a colon, indicating extended op syntax. var colon, mask int colon = strings.IndexRune(param, ':') if colon > -1 && len(param) > colon+1 { mask = colon + 1 } else { colon = -1 } // Set the member this opping refers to. if target := core.GetUserByNick(param[mask:]); target != nil { if m := ch.GetMember(target); m != nil { change.Member = m } else { return nil } } else { return nil } // If a colon exists, treat everything before it as opflags. if colon > -1 { change.Data = "" for _, char := range param[:colon] { if v := Flags.Str(char); v != "" { if change.Data != "" { change.Data += " " } change.Data += v } } } // Get the existing op flags, expanding default ops. existingData := change.Member.Data("op") words := strings.Fields(existingData) var existing string for _, word := range words { if word == "on" { word = perm.DefaultChanOp() } if existing != "" { existing += " " } existing += word } if adding { // If we're adding the ban, add the new restrictions to the // previous restrictions. if existing != "" { words := strings.Fields(existing) remwords := strings.Fields(change.Data) for _, flag := range words { var found bool for _, w := range remwords { if w == flag { found = true break } } if !found { if change.Data != "" { change.Data += " " } change.Data += flag } } } } else { // If we're removing the ban, remove the removed restrictions. // Leave restrictions not removed alone. // This is "fun". This is also O(n^2). var left string if existing != "" { words := strings.Fields(existing) remwords := strings.Fields(change.Data) for _, flag := range words { var found bool for _, w := range remwords { if w == flag { found = true break } } if !found && Flags.Char(flag) != 0 { if left != "" { left += " " } left += flag } } } change.Data = left } // Test for whether we have a default restriction. var outsideDefault bool var missingDefault bool defwords := strings.Fields(perm.DefaultChanOp()) words = strings.Fields(change.Data) for _, word := range words { var found bool for _, defword := range defwords { if defword == word { found = true break } } if !found { outsideDefault = true break } } if !outsideDefault { for _, defword := range defwords { var found bool for _, word := range words { if defword == word { found = true break } } if !found { missingDefault = true break } } } // If the restriction is the same as the default restriction, quietly // change it to "on". if !outsideDefault && !missingDefault { change.Data = "on" } return []core.DataChange{change} }