Example #1
0
// 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
}
Example #2
0
// 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}
}