// 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 }
// Handle a single (potential) server link. // outgoing indicates whether it is outgoing or incoming. func link(c *net.TCPConn, outgoing bool) { errMsg := "Input Error" serverMutex.Lock() // Create a new server, and add it to the server list. l := new(local) l.local = l l.next = servers if servers != nil { servers.prev = &(l.server) } servers = &(l.server) l.c = c serverMutex.Unlock() // Defer deletion of the server. If it's already deleted, no harm done. defer func() { l.Delete(errMsg) }() if outgoing { link_auth(l) } errMsg = irc.ReadLine(l.c, make([]byte, 20960), func(line []byte) { // Parse the line. prefix, command, params, perr := irc.Parse(commands, line, l.authed) // Look up the server or user this command is from. var source interface{} if len(prefix) == 9 { u := core.GetUser(string(prefix)) if u == nil { source = nil } else if u.Owner() != me { source = nil } else { userver := u.Owndata().(*server) if userver.local == l { source = u } } } else if len(prefix) == 3 { v := core.GetSID(string(prefix)) if v == nil { source = nil } else if s, ok := v.(*server); ok { if s.local == l { source = s } } } else if len(prefix) == 0 { // No prefix; it's from this server. source = &(l.server) } else { // Prefix is gibberish. source = nil } // If we successfully got a command and source, run it. if source != nil && command != nil { command.Handler(source, params) } else if perr != nil { // The IRC protocol is stupid. switch perr.Num { case irc.CmdNotFound: irc.SendLine(l, from(nil), l.sid, "421", "%s :%s", perr.CmdName, perr) case irc.CmdForRegistered: irc.SendFrom(l, from(nil), "451 %s :%s", perr.CmdName, perr) case irc.CmdForUnregistered: irc.SendFrom(l, from(nil), "462 %s :%s", l.sid, perr) default: irc.SendFrom(l, from(nil), "461 %s %s :%s", l.sid, perr.CmdName, perr) } } }) }