mirror of
https://github.com/ergochat/ergo.git
synced 2024-11-10 22:19:31 +01:00
Merge pull request #21 from jlatt/safer-unicoding
normalize unicode safely
This commit is contained in:
commit
e9fb5979a6
@ -3,27 +3,22 @@ package irc
|
||||
import (
|
||||
"log"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Channel struct {
|
||||
flags ChannelModeSet
|
||||
lists map[ChannelMode]*UserMaskSet
|
||||
key string
|
||||
key Text
|
||||
members MemberSet
|
||||
name string
|
||||
name Name
|
||||
server *Server
|
||||
topic string
|
||||
topic Text
|
||||
userLimit uint64
|
||||
}
|
||||
|
||||
func IsChannel(target string) bool {
|
||||
return ChannelNameExpr.MatchString(target)
|
||||
}
|
||||
|
||||
// NewChannel creates a new channel from a `Server` and a `name`
|
||||
// string, which must be unique on the server.
|
||||
func NewChannel(s *Server, name string) *Channel {
|
||||
func NewChannel(s *Server, name Name) *Channel {
|
||||
channel := &Channel{
|
||||
flags: make(ChannelModeSet),
|
||||
lists: map[ChannelMode]*UserMaskSet{
|
||||
@ -32,7 +27,7 @@ func NewChannel(s *Server, name string) *Channel {
|
||||
InviteMask: NewUserMaskSet(),
|
||||
},
|
||||
members: make(MemberSet),
|
||||
name: strings.ToLower(name),
|
||||
name: name,
|
||||
server: s,
|
||||
}
|
||||
|
||||
@ -73,22 +68,22 @@ func (channel *Channel) Nicks(target *Client) []string {
|
||||
nicks[i] += "+"
|
||||
}
|
||||
}
|
||||
nicks[i] += client.Nick()
|
||||
nicks[i] += client.Nick().String()
|
||||
i += 1
|
||||
}
|
||||
return nicks
|
||||
}
|
||||
|
||||
func (channel *Channel) Id() string {
|
||||
func (channel *Channel) Id() Name {
|
||||
return channel.name
|
||||
}
|
||||
|
||||
func (channel *Channel) Nick() string {
|
||||
func (channel *Channel) Nick() Name {
|
||||
return channel.name
|
||||
}
|
||||
|
||||
func (channel *Channel) String() string {
|
||||
return channel.Id()
|
||||
return channel.Id().String()
|
||||
}
|
||||
|
||||
// <mode> <mode params>
|
||||
@ -117,7 +112,7 @@ func (channel *Channel) ModeString(client *Client) (str string) {
|
||||
// args for flags with args: The order must match above to keep
|
||||
// positional arguments in place.
|
||||
if showKey {
|
||||
str += " " + channel.key
|
||||
str += " " + channel.key.String()
|
||||
}
|
||||
if showUserLimit {
|
||||
str += " " + strconv.FormatUint(channel.userLimit, 10)
|
||||
@ -131,11 +126,11 @@ func (channel *Channel) IsFull() bool {
|
||||
(uint64(len(channel.members)) >= channel.userLimit)
|
||||
}
|
||||
|
||||
func (channel *Channel) CheckKey(key string) bool {
|
||||
func (channel *Channel) CheckKey(key Text) bool {
|
||||
return (channel.key == "") || (channel.key == key)
|
||||
}
|
||||
|
||||
func (channel *Channel) Join(client *Client, key string) {
|
||||
func (channel *Channel) Join(client *Client, key Text) {
|
||||
if channel.members.Has(client) {
|
||||
// already joined, no message?
|
||||
return
|
||||
@ -179,7 +174,7 @@ func (channel *Channel) Join(client *Client, key string) {
|
||||
channel.Names(client)
|
||||
}
|
||||
|
||||
func (channel *Channel) Part(client *Client, message string) {
|
||||
func (channel *Channel) Part(client *Client, message Text) {
|
||||
if !channel.members.Has(client) {
|
||||
client.ErrNotOnChannel(channel)
|
||||
return
|
||||
@ -207,7 +202,7 @@ func (channel *Channel) GetTopic(client *Client) {
|
||||
client.RplTopic(channel)
|
||||
}
|
||||
|
||||
func (channel *Channel) SetTopic(client *Client, topic string) {
|
||||
func (channel *Channel) SetTopic(client *Client, topic Text) {
|
||||
if !(client.flags[Operator] || channel.members.Has(client)) {
|
||||
client.ErrNotOnChannel(channel)
|
||||
return
|
||||
@ -244,7 +239,7 @@ func (channel *Channel) CanSpeak(client *Client) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (channel *Channel) PrivMsg(client *Client, message string) {
|
||||
func (channel *Channel) PrivMsg(client *Client, message Text) {
|
||||
if !channel.CanSpeak(client) {
|
||||
client.ErrCannotSendToChan(channel)
|
||||
return
|
||||
@ -283,7 +278,7 @@ func (channel *Channel) applyModeFlag(client *Client, mode ChannelMode,
|
||||
}
|
||||
|
||||
func (channel *Channel) applyModeMember(client *Client, mode ChannelMode,
|
||||
op ModeOp, nick string) bool {
|
||||
op ModeOp, nick Name) bool {
|
||||
if !channel.ClientIsOperator(client) {
|
||||
client.ErrChanOPrivIsNeeded(channel)
|
||||
return false
|
||||
@ -331,7 +326,7 @@ func (channel *Channel) ShowMaskList(client *Client, mode ChannelMode) {
|
||||
}
|
||||
|
||||
func (channel *Channel) applyModeMask(client *Client, mode ChannelMode, op ModeOp,
|
||||
mask string) bool {
|
||||
mask Name) bool {
|
||||
list := channel.lists[mode]
|
||||
if list == nil {
|
||||
// This should never happen, but better safe than panicky.
|
||||
@ -362,7 +357,8 @@ func (channel *Channel) applyModeMask(client *Client, mode ChannelMode, op ModeO
|
||||
func (channel *Channel) applyMode(client *Client, change *ChannelModeChange) bool {
|
||||
switch change.mode {
|
||||
case BanMask, ExceptMask, InviteMask:
|
||||
return channel.applyModeMask(client, change.mode, change.op, change.arg)
|
||||
return channel.applyModeMask(client, change.mode, change.op,
|
||||
NewName(change.arg))
|
||||
|
||||
case InviteOnly, Moderated, NoOutside, OpOnlyTopic, Persistent, Private:
|
||||
return channel.applyModeFlag(client, change.mode, change.op)
|
||||
@ -379,11 +375,12 @@ func (channel *Channel) applyMode(client *Client, change *ChannelModeChange) boo
|
||||
client.ErrNeedMoreParams("MODE")
|
||||
return false
|
||||
}
|
||||
if change.arg == channel.key {
|
||||
key := NewText(change.arg)
|
||||
if key == channel.key {
|
||||
return false
|
||||
}
|
||||
|
||||
channel.key = change.arg
|
||||
channel.key = key
|
||||
return true
|
||||
|
||||
case Remove:
|
||||
@ -405,7 +402,8 @@ func (channel *Channel) applyMode(client *Client, change *ChannelModeChange) boo
|
||||
return true
|
||||
|
||||
case ChannelOperator, Voice:
|
||||
return channel.applyModeMember(client, change.mode, change.op, change.arg)
|
||||
return channel.applyModeMember(client, change.mode, change.op,
|
||||
NewName(change.arg))
|
||||
|
||||
default:
|
||||
client.ErrUnknownMode(change.mode, channel)
|
||||
@ -456,7 +454,7 @@ func (channel *Channel) Persist() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (channel *Channel) Notice(client *Client, message string) {
|
||||
func (channel *Channel) Notice(client *Client, message Text) {
|
||||
if !channel.CanSpeak(client) {
|
||||
client.ErrCannotSendToChan(channel)
|
||||
return
|
||||
@ -478,7 +476,7 @@ func (channel *Channel) Quit(client *Client) {
|
||||
}
|
||||
}
|
||||
|
||||
func (channel *Channel) Kick(client *Client, target *Client, comment string) {
|
||||
func (channel *Channel) Kick(client *Client, target *Client, comment Text) {
|
||||
if !(client.flags[Operator] || channel.members.Has(client)) {
|
||||
client.ErrNotOnChannel(channel)
|
||||
return
|
||||
|
@ -7,14 +7,10 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
func IsNickname(nick string) bool {
|
||||
return NicknameExpr.MatchString(nick)
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
atime time.Time
|
||||
authorized bool
|
||||
awayMessage string
|
||||
awayMessage Text
|
||||
capabilities CapabilitySet
|
||||
capState CapState
|
||||
channels ChannelSet
|
||||
@ -23,16 +19,16 @@ type Client struct {
|
||||
flags map[UserMode]bool
|
||||
hasQuit bool
|
||||
hops uint
|
||||
hostname string
|
||||
hostname Name
|
||||
idleTimer *time.Timer
|
||||
loginTimer *time.Timer
|
||||
nick string
|
||||
nick Name
|
||||
phase Phase
|
||||
quitTimer *time.Timer
|
||||
realname string
|
||||
realname Text
|
||||
server *Server
|
||||
socket *Socket
|
||||
username string
|
||||
username Name
|
||||
}
|
||||
|
||||
func NewClient(server *Server, conn net.Conn) *Client {
|
||||
@ -186,27 +182,27 @@ func (c *Client) ModeString() (str string) {
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Client) UserHost() string {
|
||||
func (c *Client) UserHost() Name {
|
||||
username := "*"
|
||||
if c.HasUsername() {
|
||||
username = c.username
|
||||
username = c.username.String()
|
||||
}
|
||||
return fmt.Sprintf("%s!%s@%s", c.Nick(), username, c.hostname)
|
||||
return Name(fmt.Sprintf("%s!%s@%s", c.Nick(), username, c.hostname))
|
||||
}
|
||||
|
||||
func (c *Client) Nick() string {
|
||||
func (c *Client) Nick() Name {
|
||||
if c.HasNick() {
|
||||
return c.nick
|
||||
}
|
||||
return "*"
|
||||
return Name("*")
|
||||
}
|
||||
|
||||
func (c *Client) Id() string {
|
||||
func (c *Client) Id() Name {
|
||||
return c.UserHost()
|
||||
}
|
||||
|
||||
func (c *Client) String() string {
|
||||
return c.Id()
|
||||
return c.Id().String()
|
||||
}
|
||||
|
||||
func (client *Client) Friends() ClientSet {
|
||||
@ -220,12 +216,12 @@ func (client *Client) Friends() ClientSet {
|
||||
return friends
|
||||
}
|
||||
|
||||
func (client *Client) SetNickname(nickname string) {
|
||||
func (client *Client) SetNickname(nickname Name) {
|
||||
client.nick = nickname
|
||||
client.server.clients.Add(client)
|
||||
}
|
||||
|
||||
func (client *Client) ChangeNickname(nickname string) {
|
||||
func (client *Client) ChangeNickname(nickname Name) {
|
||||
// Make reply before changing nick to capture original source id.
|
||||
reply := RplNick(client, nickname)
|
||||
client.server.clients.Remove(client)
|
||||
@ -244,7 +240,7 @@ func (client *Client) Reply(reply string, args ...interface{}) {
|
||||
client.socket.Write(reply)
|
||||
}
|
||||
|
||||
func (client *Client) Quit(message string) {
|
||||
func (client *Client) Quit(message Text) {
|
||||
if client.hasQuit {
|
||||
return
|
||||
}
|
||||
|
@ -25,36 +25,36 @@ func HasWildcards(mask string) bool {
|
||||
return wildMaskExpr.MatchString(mask)
|
||||
}
|
||||
|
||||
func ExpandUserHost(userhost string) (expanded string) {
|
||||
func ExpandUserHost(userhost Name) (expanded Name) {
|
||||
expanded = userhost
|
||||
// fill in missing wildcards for nicks
|
||||
if !strings.Contains(expanded, "!") {
|
||||
if !strings.Contains(expanded.String(), "!") {
|
||||
expanded += "!*"
|
||||
}
|
||||
if !strings.Contains(expanded, "@") {
|
||||
if !strings.Contains(expanded.String(), "@") {
|
||||
expanded += "@*"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func QuoteLike(userhost string) string {
|
||||
return likeQuoter.Replace(userhost)
|
||||
func QuoteLike(userhost Name) Name {
|
||||
return Name(likeQuoter.Replace(userhost.String()))
|
||||
}
|
||||
|
||||
type ClientLookupSet struct {
|
||||
byNick map[string]*Client
|
||||
byNick map[Name]*Client
|
||||
db *ClientDB
|
||||
}
|
||||
|
||||
func NewClientLookupSet() *ClientLookupSet {
|
||||
return &ClientLookupSet{
|
||||
byNick: make(map[string]*Client),
|
||||
byNick: make(map[Name]*Client),
|
||||
db: NewClientDB(),
|
||||
}
|
||||
}
|
||||
|
||||
func (clients *ClientLookupSet) Get(nick string) *Client {
|
||||
return clients.byNick[strings.ToLower(nick)]
|
||||
func (clients *ClientLookupSet) Get(nick Name) *Client {
|
||||
return clients.byNick[nick.ToLower()]
|
||||
}
|
||||
|
||||
func (clients *ClientLookupSet) Add(client *Client) error {
|
||||
@ -64,7 +64,7 @@ func (clients *ClientLookupSet) Add(client *Client) error {
|
||||
if clients.Get(client.nick) != nil {
|
||||
return ErrNicknameInUse
|
||||
}
|
||||
clients.byNick[strings.ToLower(client.nick)] = client
|
||||
clients.byNick[client.Nick().ToLower()] = client
|
||||
clients.db.Add(client)
|
||||
return nil
|
||||
}
|
||||
@ -76,12 +76,12 @@ func (clients *ClientLookupSet) Remove(client *Client) error {
|
||||
if clients.Get(client.nick) != client {
|
||||
return ErrNicknameMismatch
|
||||
}
|
||||
delete(clients.byNick, strings.ToLower(client.nick))
|
||||
delete(clients.byNick, client.nick.ToLower())
|
||||
clients.db.Remove(client)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (clients *ClientLookupSet) FindAll(userhost string) (set ClientSet) {
|
||||
func (clients *ClientLookupSet) FindAll(userhost Name) (set ClientSet) {
|
||||
userhost = ExpandUserHost(userhost)
|
||||
set = make(ClientSet)
|
||||
rows, err := clients.db.db.Query(
|
||||
@ -94,7 +94,7 @@ func (clients *ClientLookupSet) FindAll(userhost string) (set ClientSet) {
|
||||
return
|
||||
}
|
||||
for rows.Next() {
|
||||
var nickname string
|
||||
var nickname Name
|
||||
err := rows.Scan(&nickname)
|
||||
if err != nil {
|
||||
if DEBUG_SERVER {
|
||||
@ -114,12 +114,12 @@ func (clients *ClientLookupSet) FindAll(userhost string) (set ClientSet) {
|
||||
return
|
||||
}
|
||||
|
||||
func (clients *ClientLookupSet) Find(userhost string) *Client {
|
||||
func (clients *ClientLookupSet) Find(userhost Name) *Client {
|
||||
userhost = ExpandUserHost(userhost)
|
||||
row := clients.db.db.QueryRow(
|
||||
`SELECT nickname FROM client WHERE userhost LIKE ? ESCAPE '\' LIMIT 1`,
|
||||
QuoteLike(userhost))
|
||||
var nickname string
|
||||
var nickname Name
|
||||
err := row.Scan(&nickname)
|
||||
if err != nil {
|
||||
if DEBUG_SERVER {
|
||||
@ -184,17 +184,17 @@ func (db *ClientDB) Remove(client *Client) {
|
||||
//
|
||||
|
||||
type UserMaskSet struct {
|
||||
masks map[string]bool
|
||||
masks map[Name]bool
|
||||
regexp *regexp.Regexp
|
||||
}
|
||||
|
||||
func NewUserMaskSet() *UserMaskSet {
|
||||
return &UserMaskSet{
|
||||
masks: make(map[string]bool),
|
||||
masks: make(map[Name]bool),
|
||||
}
|
||||
}
|
||||
|
||||
func (set *UserMaskSet) Add(mask string) bool {
|
||||
func (set *UserMaskSet) Add(mask Name) bool {
|
||||
if set.masks[mask] {
|
||||
return false
|
||||
}
|
||||
@ -203,7 +203,7 @@ func (set *UserMaskSet) Add(mask string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (set *UserMaskSet) AddAll(masks []string) (added bool) {
|
||||
func (set *UserMaskSet) AddAll(masks []Name) (added bool) {
|
||||
for _, mask := range masks {
|
||||
if !added && !set.masks[mask] {
|
||||
added = true
|
||||
@ -214,7 +214,7 @@ func (set *UserMaskSet) AddAll(masks []string) (added bool) {
|
||||
return
|
||||
}
|
||||
|
||||
func (set *UserMaskSet) Remove(mask string) bool {
|
||||
func (set *UserMaskSet) Remove(mask Name) bool {
|
||||
if !set.masks[mask] {
|
||||
return false
|
||||
}
|
||||
@ -223,18 +223,18 @@ func (set *UserMaskSet) Remove(mask string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (set *UserMaskSet) Match(userhost string) bool {
|
||||
func (set *UserMaskSet) Match(userhost Name) bool {
|
||||
if set.regexp == nil {
|
||||
return false
|
||||
}
|
||||
return set.regexp.MatchString(userhost)
|
||||
return set.regexp.MatchString(userhost.String())
|
||||
}
|
||||
|
||||
func (set *UserMaskSet) String() string {
|
||||
masks := make([]string, len(set.masks))
|
||||
index := 0
|
||||
for mask := range set.masks {
|
||||
masks[index] = mask
|
||||
masks[index] = mask.String()
|
||||
index += 1
|
||||
}
|
||||
return strings.Join(masks, " ")
|
||||
@ -255,7 +255,7 @@ func (set *UserMaskSet) setRegexp() {
|
||||
maskExprs := make([]string, len(set.masks))
|
||||
index := 0
|
||||
for mask := range set.masks {
|
||||
manyParts := strings.Split(mask, "*")
|
||||
manyParts := strings.Split(mask.String(), "*")
|
||||
manyExprs := make([]string, len(manyParts))
|
||||
for mindex, manyPart := range manyParts {
|
||||
oneParts := strings.Split(manyPart, "?")
|
||||
|
235
irc/commands.go
235
irc/commands.go
@ -1,7 +1,6 @@
|
||||
package irc
|
||||
|
||||
import (
|
||||
"code.google.com/p/go.text/unicode/norm"
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
@ -114,14 +113,14 @@ func ParseLine(line string) (command StringCode, args []string) {
|
||||
_, line = splitArg(line)
|
||||
}
|
||||
arg, line := splitArg(line)
|
||||
command = StringCode(strings.ToUpper(arg))
|
||||
command = StringCode(NewName(strings.ToUpper(arg)))
|
||||
for len(line) > 0 {
|
||||
if strings.HasPrefix(line, ":") {
|
||||
args = append(args, norm.NFC.String(line[len(":"):]))
|
||||
args = append(args, line[len(":"):])
|
||||
break
|
||||
}
|
||||
arg, line = splitArg(line)
|
||||
args = append(args, norm.NFKC.String(arg))
|
||||
args = append(args, arg)
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -147,8 +146,8 @@ func NewUnknownCommand(args []string) *UnknownCommand {
|
||||
|
||||
type PingCommand struct {
|
||||
BaseCommand
|
||||
server string
|
||||
server2 string
|
||||
server Name
|
||||
server2 Name
|
||||
}
|
||||
|
||||
func (cmd *PingCommand) String() string {
|
||||
@ -160,10 +159,10 @@ func NewPingCommand(args []string) (editableCommand, error) {
|
||||
return nil, NotEnoughArgsError
|
||||
}
|
||||
msg := &PingCommand{
|
||||
server: args[0],
|
||||
server: NewName(args[0]),
|
||||
}
|
||||
if len(args) > 1 {
|
||||
msg.server2 = args[1]
|
||||
msg.server2 = NewName(args[1])
|
||||
}
|
||||
return msg, nil
|
||||
}
|
||||
@ -172,8 +171,8 @@ func NewPingCommand(args []string) (editableCommand, error) {
|
||||
|
||||
type PongCommand struct {
|
||||
BaseCommand
|
||||
server1 string
|
||||
server2 string
|
||||
server1 Name
|
||||
server2 Name
|
||||
}
|
||||
|
||||
func (cmd *PongCommand) String() string {
|
||||
@ -185,10 +184,10 @@ func NewPongCommand(args []string) (editableCommand, error) {
|
||||
return nil, NotEnoughArgsError
|
||||
}
|
||||
message := &PongCommand{
|
||||
server1: args[0],
|
||||
server1: NewName(args[0]),
|
||||
}
|
||||
if len(args) > 1 {
|
||||
message.server2 = args[1]
|
||||
message.server2 = NewName(args[1])
|
||||
}
|
||||
return message, nil
|
||||
}
|
||||
@ -230,7 +229,7 @@ func NewPassCommand(args []string) (editableCommand, error) {
|
||||
|
||||
type NickCommand struct {
|
||||
BaseCommand
|
||||
nickname string
|
||||
nickname Name
|
||||
}
|
||||
|
||||
func (m *NickCommand) String() string {
|
||||
@ -242,21 +241,21 @@ func NewNickCommand(args []string) (editableCommand, error) {
|
||||
return nil, NotEnoughArgsError
|
||||
}
|
||||
return &NickCommand{
|
||||
nickname: args[0],
|
||||
nickname: NewName(args[0]),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type UserCommand struct {
|
||||
BaseCommand
|
||||
username string
|
||||
realname string
|
||||
username Name
|
||||
realname Text
|
||||
}
|
||||
|
||||
// USER <username> <hostname> <servername> <realname>
|
||||
type RFC1459UserCommand struct {
|
||||
UserCommand
|
||||
hostname string
|
||||
servername string
|
||||
hostname Name
|
||||
servername Name
|
||||
}
|
||||
|
||||
func (cmd *RFC1459UserCommand) String() string {
|
||||
@ -297,17 +296,17 @@ func NewUserCommand(args []string) (editableCommand, error) {
|
||||
mode: uint8(mode),
|
||||
unused: args[2],
|
||||
}
|
||||
msg.username = args[0]
|
||||
msg.realname = args[3]
|
||||
msg.username = NewName(args[0])
|
||||
msg.realname = NewText(args[3])
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
msg := &RFC1459UserCommand{
|
||||
hostname: args[1],
|
||||
servername: args[2],
|
||||
hostname: NewName(args[1]),
|
||||
servername: NewName(args[2]),
|
||||
}
|
||||
msg.username = args[0]
|
||||
msg.realname = args[3]
|
||||
msg.username = NewName(args[0])
|
||||
msg.realname = NewText(args[3])
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
@ -315,7 +314,7 @@ func NewUserCommand(args []string) (editableCommand, error) {
|
||||
|
||||
type QuitCommand struct {
|
||||
BaseCommand
|
||||
message string
|
||||
message Text
|
||||
}
|
||||
|
||||
func (cmd *QuitCommand) String() string {
|
||||
@ -325,7 +324,7 @@ func (cmd *QuitCommand) String() string {
|
||||
func NewQuitCommand(args []string) (editableCommand, error) {
|
||||
msg := &QuitCommand{}
|
||||
if len(args) > 0 {
|
||||
msg.message = args[0]
|
||||
msg.message = NewText(args[0])
|
||||
}
|
||||
return msg, nil
|
||||
}
|
||||
@ -334,7 +333,7 @@ func NewQuitCommand(args []string) (editableCommand, error) {
|
||||
|
||||
type JoinCommand struct {
|
||||
BaseCommand
|
||||
channels map[string]string
|
||||
channels map[Name]Text
|
||||
zero bool
|
||||
}
|
||||
|
||||
@ -344,7 +343,7 @@ func (cmd *JoinCommand) String() string {
|
||||
|
||||
func NewJoinCommand(args []string) (editableCommand, error) {
|
||||
msg := &JoinCommand{
|
||||
channels: make(map[string]string),
|
||||
channels: make(map[Name]Text),
|
||||
}
|
||||
|
||||
if len(args) == 0 {
|
||||
@ -364,7 +363,7 @@ func NewJoinCommand(args []string) (editableCommand, error) {
|
||||
}
|
||||
}
|
||||
for i, channel := range channels {
|
||||
msg.channels[channel] = keys[i]
|
||||
msg.channels[NewName(channel)] = NewText(keys[i])
|
||||
}
|
||||
|
||||
return msg, nil
|
||||
@ -374,13 +373,13 @@ func NewJoinCommand(args []string) (editableCommand, error) {
|
||||
|
||||
type PartCommand struct {
|
||||
BaseCommand
|
||||
channels []string
|
||||
message string
|
||||
channels []Name
|
||||
message Text
|
||||
}
|
||||
|
||||
func (cmd *PartCommand) Message() string {
|
||||
func (cmd *PartCommand) Message() Text {
|
||||
if cmd.message == "" {
|
||||
return cmd.Client().Nick()
|
||||
return cmd.Client().Nick().Text()
|
||||
}
|
||||
return cmd.message
|
||||
}
|
||||
@ -394,10 +393,10 @@ func NewPartCommand(args []string) (editableCommand, error) {
|
||||
return nil, NotEnoughArgsError
|
||||
}
|
||||
msg := &PartCommand{
|
||||
channels: strings.Split(args[0], ","),
|
||||
channels: NewNames(strings.Split(args[0], ",")),
|
||||
}
|
||||
if len(args) > 1 {
|
||||
msg.message = args[1]
|
||||
msg.message = NewText(args[1])
|
||||
}
|
||||
return msg, nil
|
||||
}
|
||||
@ -406,8 +405,8 @@ func NewPartCommand(args []string) (editableCommand, error) {
|
||||
|
||||
type PrivMsgCommand struct {
|
||||
BaseCommand
|
||||
target string
|
||||
message string
|
||||
target Name
|
||||
message Text
|
||||
}
|
||||
|
||||
func (cmd *PrivMsgCommand) String() string {
|
||||
@ -419,8 +418,8 @@ func NewPrivMsgCommand(args []string) (editableCommand, error) {
|
||||
return nil, NotEnoughArgsError
|
||||
}
|
||||
return &PrivMsgCommand{
|
||||
target: args[0],
|
||||
message: args[1],
|
||||
target: NewName(args[0]),
|
||||
message: NewText(args[1]),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -428,9 +427,9 @@ func NewPrivMsgCommand(args []string) (editableCommand, error) {
|
||||
|
||||
type TopicCommand struct {
|
||||
BaseCommand
|
||||
channel string
|
||||
channel Name
|
||||
setTopic bool
|
||||
topic string
|
||||
topic Text
|
||||
}
|
||||
|
||||
func (cmd *TopicCommand) String() string {
|
||||
@ -442,11 +441,11 @@ func NewTopicCommand(args []string) (editableCommand, error) {
|
||||
return nil, NotEnoughArgsError
|
||||
}
|
||||
msg := &TopicCommand{
|
||||
channel: args[0],
|
||||
channel: NewName(args[0]),
|
||||
}
|
||||
if len(args) > 1 {
|
||||
msg.setTopic = true
|
||||
msg.topic = args[1]
|
||||
msg.topic = NewText(args[1])
|
||||
}
|
||||
return msg, nil
|
||||
}
|
||||
@ -482,18 +481,18 @@ func (changes ModeChanges) String() string {
|
||||
|
||||
type ModeCommand struct {
|
||||
BaseCommand
|
||||
nickname string
|
||||
nickname Name
|
||||
changes ModeChanges
|
||||
}
|
||||
|
||||
// MODE <nickname> *( ( "+" / "-" ) *( "i" / "w" / "o" / "O" / "r" ) )
|
||||
func NewUserModeCommand(args []string) (editableCommand, error) {
|
||||
func NewUserModeCommand(nickname Name, args []string) (editableCommand, error) {
|
||||
cmd := &ModeCommand{
|
||||
nickname: args[0],
|
||||
nickname: nickname,
|
||||
changes: make(ModeChanges, 0),
|
||||
}
|
||||
|
||||
for _, modeChange := range args[1:] {
|
||||
for _, modeChange := range args {
|
||||
if len(modeChange) == 0 {
|
||||
continue
|
||||
}
|
||||
@ -559,17 +558,16 @@ func (changes ChannelModeChanges) String() (str string) {
|
||||
|
||||
type ChannelModeCommand struct {
|
||||
BaseCommand
|
||||
channel string
|
||||
channel Name
|
||||
changes ChannelModeChanges
|
||||
}
|
||||
|
||||
// MODE <channel> *( ( "-" / "+" ) *<modes> *<modeparams> )
|
||||
func NewChannelModeCommand(args []string) (editableCommand, error) {
|
||||
func NewChannelModeCommand(channel Name, args []string) (editableCommand, error) {
|
||||
cmd := &ChannelModeCommand{
|
||||
channel: args[0],
|
||||
channel: channel,
|
||||
changes: make(ChannelModeChanges, 0),
|
||||
}
|
||||
args = args[1:]
|
||||
|
||||
for len(args) > 0 {
|
||||
if len(args[0]) == 0 {
|
||||
@ -616,17 +614,18 @@ func NewModeCommand(args []string) (editableCommand, error) {
|
||||
return nil, NotEnoughArgsError
|
||||
}
|
||||
|
||||
if IsChannel(args[0]) {
|
||||
return NewChannelModeCommand(args)
|
||||
name := NewName(args[0])
|
||||
if name.IsChannel() {
|
||||
return NewChannelModeCommand(name, args[1:])
|
||||
} else {
|
||||
return NewUserModeCommand(args)
|
||||
return NewUserModeCommand(name, args[1:])
|
||||
}
|
||||
}
|
||||
|
||||
type WhoisCommand struct {
|
||||
BaseCommand
|
||||
target string
|
||||
masks []string
|
||||
target Name
|
||||
masks []Name
|
||||
}
|
||||
|
||||
// WHOIS [ <target> ] <mask> *( "," <mask> )
|
||||
@ -646,8 +645,8 @@ func NewWhoisCommand(args []string) (editableCommand, error) {
|
||||
}
|
||||
|
||||
return &WhoisCommand{
|
||||
target: target,
|
||||
masks: strings.Split(masks, ","),
|
||||
target: NewName(target),
|
||||
masks: NewNames(strings.Split(masks, ",")),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -657,7 +656,7 @@ func (msg *WhoisCommand) String() string {
|
||||
|
||||
type WhoCommand struct {
|
||||
BaseCommand
|
||||
mask string
|
||||
mask Name
|
||||
operatorOnly bool
|
||||
}
|
||||
|
||||
@ -666,7 +665,7 @@ func NewWhoCommand(args []string) (editableCommand, error) {
|
||||
cmd := &WhoCommand{}
|
||||
|
||||
if len(args) > 0 {
|
||||
cmd.mask = args[0]
|
||||
cmd.mask = NewName(args[0])
|
||||
}
|
||||
|
||||
if (len(args) > 1) && (args[1] == "o") {
|
||||
@ -682,7 +681,7 @@ func (msg *WhoCommand) String() string {
|
||||
|
||||
type OperCommand struct {
|
||||
PassCommand
|
||||
name string
|
||||
name Name
|
||||
}
|
||||
|
||||
func (msg *OperCommand) String() string {
|
||||
@ -700,7 +699,7 @@ func NewOperCommand(args []string) (editableCommand, error) {
|
||||
}
|
||||
|
||||
cmd := &OperCommand{
|
||||
name: args[0],
|
||||
name: NewName(args[0]),
|
||||
}
|
||||
cmd.password = []byte(args[1])
|
||||
return cmd, nil
|
||||
@ -739,12 +738,12 @@ func NewCapCommand(args []string) (editableCommand, error) {
|
||||
// HAPROXY support
|
||||
type ProxyCommand struct {
|
||||
BaseCommand
|
||||
net string
|
||||
sourceIP string
|
||||
destIP string
|
||||
sourcePort string
|
||||
destPort string
|
||||
hostname string // looked up in socket thread
|
||||
net Name
|
||||
sourceIP Name
|
||||
destIP Name
|
||||
sourcePort Name
|
||||
destPort Name
|
||||
hostname Name // looked up in socket thread
|
||||
}
|
||||
|
||||
func (msg *ProxyCommand) String() string {
|
||||
@ -756,18 +755,18 @@ func NewProxyCommand(args []string) (editableCommand, error) {
|
||||
return nil, NotEnoughArgsError
|
||||
}
|
||||
return &ProxyCommand{
|
||||
net: args[0],
|
||||
sourceIP: args[1],
|
||||
destIP: args[2],
|
||||
sourcePort: args[3],
|
||||
destPort: args[4],
|
||||
hostname: LookupHostname(args[1]),
|
||||
net: NewName(args[0]),
|
||||
sourceIP: NewName(args[1]),
|
||||
destIP: NewName(args[2]),
|
||||
sourcePort: NewName(args[3]),
|
||||
destPort: NewName(args[4]),
|
||||
hostname: LookupHostname(NewName(args[1])),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type AwayCommand struct {
|
||||
BaseCommand
|
||||
text string
|
||||
text Text
|
||||
away bool
|
||||
}
|
||||
|
||||
@ -779,7 +778,7 @@ func NewAwayCommand(args []string) (editableCommand, error) {
|
||||
cmd := &AwayCommand{}
|
||||
|
||||
if len(args) > 0 {
|
||||
cmd.text = args[0]
|
||||
cmd.text = NewText(args[0])
|
||||
cmd.away = true
|
||||
}
|
||||
|
||||
@ -788,7 +787,7 @@ func NewAwayCommand(args []string) (editableCommand, error) {
|
||||
|
||||
type IsOnCommand struct {
|
||||
BaseCommand
|
||||
nicks []string
|
||||
nicks []Name
|
||||
}
|
||||
|
||||
func (msg *IsOnCommand) String() string {
|
||||
@ -801,27 +800,27 @@ func NewIsOnCommand(args []string) (editableCommand, error) {
|
||||
}
|
||||
|
||||
return &IsOnCommand{
|
||||
nicks: args,
|
||||
nicks: NewNames(args),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type MOTDCommand struct {
|
||||
BaseCommand
|
||||
target string
|
||||
target Name
|
||||
}
|
||||
|
||||
func NewMOTDCommand(args []string) (editableCommand, error) {
|
||||
cmd := &MOTDCommand{}
|
||||
if len(args) > 0 {
|
||||
cmd.target = args[0]
|
||||
cmd.target = NewName(args[0])
|
||||
}
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
type NoticeCommand struct {
|
||||
BaseCommand
|
||||
target string
|
||||
message string
|
||||
target Name
|
||||
message Text
|
||||
}
|
||||
|
||||
func (cmd *NoticeCommand) String() string {
|
||||
@ -833,20 +832,20 @@ func NewNoticeCommand(args []string) (editableCommand, error) {
|
||||
return nil, NotEnoughArgsError
|
||||
}
|
||||
return &NoticeCommand{
|
||||
target: args[0],
|
||||
message: args[1],
|
||||
target: NewName(args[0]),
|
||||
message: NewText(args[1]),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type KickCommand struct {
|
||||
BaseCommand
|
||||
kicks map[string]string
|
||||
comment string
|
||||
kicks map[Name]Name
|
||||
comment Text
|
||||
}
|
||||
|
||||
func (msg *KickCommand) Comment() string {
|
||||
func (msg *KickCommand) Comment() Text {
|
||||
if msg.comment == "" {
|
||||
return msg.Client().Nick()
|
||||
return msg.Client().Nick().Text()
|
||||
}
|
||||
return msg.comment
|
||||
}
|
||||
@ -855,13 +854,13 @@ func NewKickCommand(args []string) (editableCommand, error) {
|
||||
if len(args) < 2 {
|
||||
return nil, NotEnoughArgsError
|
||||
}
|
||||
channels := strings.Split(args[0], ",")
|
||||
users := strings.Split(args[1], ",")
|
||||
channels := NewNames(strings.Split(args[0], ","))
|
||||
users := NewNames(strings.Split(args[1], ","))
|
||||
if (len(channels) != len(users)) && (len(users) != 1) {
|
||||
return nil, NotEnoughArgsError
|
||||
}
|
||||
cmd := &KickCommand{
|
||||
kicks: make(map[string]string),
|
||||
kicks: make(map[Name]Name),
|
||||
}
|
||||
for index, channel := range channels {
|
||||
if len(users) == 1 {
|
||||
@ -871,48 +870,48 @@ func NewKickCommand(args []string) (editableCommand, error) {
|
||||
}
|
||||
}
|
||||
if len(args) > 2 {
|
||||
cmd.comment = args[2]
|
||||
cmd.comment = NewText(args[2])
|
||||
}
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
type ListCommand struct {
|
||||
BaseCommand
|
||||
channels []string
|
||||
target string
|
||||
channels []Name
|
||||
target Name
|
||||
}
|
||||
|
||||
func NewListCommand(args []string) (editableCommand, error) {
|
||||
cmd := &ListCommand{}
|
||||
if len(args) > 0 {
|
||||
cmd.channels = strings.Split(args[0], ",")
|
||||
cmd.channels = NewNames(strings.Split(args[0], ","))
|
||||
}
|
||||
if len(args) > 1 {
|
||||
cmd.target = args[1]
|
||||
cmd.target = NewName(args[1])
|
||||
}
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
type NamesCommand struct {
|
||||
BaseCommand
|
||||
channels []string
|
||||
target string
|
||||
channels []Name
|
||||
target Name
|
||||
}
|
||||
|
||||
func NewNamesCommand(args []string) (editableCommand, error) {
|
||||
cmd := &NamesCommand{}
|
||||
if len(args) > 0 {
|
||||
cmd.channels = strings.Split(args[0], ",")
|
||||
cmd.channels = NewNames(strings.Split(args[0], ","))
|
||||
}
|
||||
if len(args) > 1 {
|
||||
cmd.target = args[1]
|
||||
cmd.target = NewName(args[1])
|
||||
}
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
type DebugCommand struct {
|
||||
BaseCommand
|
||||
subCommand string
|
||||
subCommand Name
|
||||
}
|
||||
|
||||
func NewDebugCommand(args []string) (editableCommand, error) {
|
||||
@ -921,27 +920,27 @@ func NewDebugCommand(args []string) (editableCommand, error) {
|
||||
}
|
||||
|
||||
return &DebugCommand{
|
||||
subCommand: strings.ToUpper(args[0]),
|
||||
subCommand: NewName(strings.ToUpper(args[0])),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type VersionCommand struct {
|
||||
BaseCommand
|
||||
target string
|
||||
target Name
|
||||
}
|
||||
|
||||
func NewVersionCommand(args []string) (editableCommand, error) {
|
||||
cmd := &VersionCommand{}
|
||||
if len(args) > 0 {
|
||||
cmd.target = args[0]
|
||||
cmd.target = NewName(args[0])
|
||||
}
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
type InviteCommand struct {
|
||||
BaseCommand
|
||||
nickname string
|
||||
channel string
|
||||
nickname Name
|
||||
channel Name
|
||||
}
|
||||
|
||||
func NewInviteCommand(args []string) (editableCommand, error) {
|
||||
@ -950,28 +949,28 @@ func NewInviteCommand(args []string) (editableCommand, error) {
|
||||
}
|
||||
|
||||
return &InviteCommand{
|
||||
nickname: args[0],
|
||||
channel: args[1],
|
||||
nickname: NewName(args[0]),
|
||||
channel: NewName(args[1]),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type TimeCommand struct {
|
||||
BaseCommand
|
||||
target string
|
||||
target Name
|
||||
}
|
||||
|
||||
func NewTimeCommand(args []string) (editableCommand, error) {
|
||||
cmd := &TimeCommand{}
|
||||
if len(args) > 0 {
|
||||
cmd.target = args[0]
|
||||
cmd.target = NewName(args[0])
|
||||
}
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
type KillCommand struct {
|
||||
BaseCommand
|
||||
nickname string
|
||||
comment string
|
||||
nickname Name
|
||||
comment Text
|
||||
}
|
||||
|
||||
func NewKillCommand(args []string) (editableCommand, error) {
|
||||
@ -979,16 +978,16 @@ func NewKillCommand(args []string) (editableCommand, error) {
|
||||
return nil, NotEnoughArgsError
|
||||
}
|
||||
return &KillCommand{
|
||||
nickname: args[0],
|
||||
comment: args[1],
|
||||
nickname: NewName(args[0]),
|
||||
comment: NewText(args[1]),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type WhoWasCommand struct {
|
||||
BaseCommand
|
||||
nicknames []string
|
||||
nicknames []Name
|
||||
count int64
|
||||
target string
|
||||
target Name
|
||||
}
|
||||
|
||||
func NewWhoWasCommand(args []string) (editableCommand, error) {
|
||||
@ -996,13 +995,13 @@ func NewWhoWasCommand(args []string) (editableCommand, error) {
|
||||
return nil, NotEnoughArgsError
|
||||
}
|
||||
cmd := &WhoWasCommand{
|
||||
nicknames: strings.Split(args[0], ","),
|
||||
nicknames: NewNames(strings.Split(args[0], ",")),
|
||||
}
|
||||
if len(args) > 1 {
|
||||
cmd.count, _ = strconv.ParseInt(args[1], 10, 64)
|
||||
}
|
||||
if len(args) > 2 {
|
||||
cmd.target = args[2]
|
||||
cmd.target = NewName(args[2])
|
||||
}
|
||||
return cmd, nil
|
||||
}
|
||||
|
@ -37,10 +37,10 @@ type Config struct {
|
||||
}
|
||||
}
|
||||
|
||||
func (conf *Config) Operators() map[string][]byte {
|
||||
operators := make(map[string][]byte)
|
||||
func (conf *Config) Operators() map[Name][]byte {
|
||||
operators := make(map[Name][]byte)
|
||||
for name, opConf := range conf.Operator {
|
||||
operators[name] = opConf.PasswordBytes()
|
||||
operators[NewName(name)] = opConf.PasswordBytes()
|
||||
}
|
||||
return operators
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package irc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"regexp"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -15,11 +14,6 @@ var (
|
||||
|
||||
// errors
|
||||
ErrAlreadyDestroyed = errors.New("already destroyed")
|
||||
|
||||
// regexps
|
||||
ChannelNameExpr = regexp.MustCompile(`^[&!#+][\pL\pN]{1,63}$`)
|
||||
NicknameExpr = regexp.MustCompile(
|
||||
"^[\\pL\\pN\\pP\\pS]{1,32}$")
|
||||
)
|
||||
|
||||
const (
|
||||
|
16
irc/net.go
16
irc/net.go
@ -5,25 +5,25 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
func IPString(addr net.Addr) string {
|
||||
func IPString(addr net.Addr) Name {
|
||||
addrStr := addr.String()
|
||||
ipaddr, _, err := net.SplitHostPort(addrStr)
|
||||
if err != nil {
|
||||
return addrStr
|
||||
return Name(addrStr)
|
||||
}
|
||||
return ipaddr
|
||||
return Name(ipaddr)
|
||||
}
|
||||
|
||||
func AddrLookupHostname(addr net.Addr) string {
|
||||
func AddrLookupHostname(addr net.Addr) Name {
|
||||
return LookupHostname(IPString(addr))
|
||||
}
|
||||
|
||||
func LookupHostname(addr string) string {
|
||||
names, err := net.LookupAddr(addr)
|
||||
func LookupHostname(addr Name) Name {
|
||||
names, err := net.LookupAddr(addr.String())
|
||||
if err != nil {
|
||||
return addr
|
||||
return Name(addr)
|
||||
}
|
||||
|
||||
hostname := strings.TrimSuffix(names[0], ".")
|
||||
return hostname
|
||||
return Name(hostname)
|
||||
}
|
||||
|
54
irc/reply.go
54
irc/reply.go
@ -79,23 +79,23 @@ func (target *Client) MultilineReply(names []string, code NumericCode, format st
|
||||
// messaging replies
|
||||
//
|
||||
|
||||
func RplPrivMsg(source Identifier, target Identifier, message string) string {
|
||||
func RplPrivMsg(source Identifier, target Identifier, message Text) string {
|
||||
return NewStringReply(source, PRIVMSG, "%s :%s", target.Nick(), message)
|
||||
}
|
||||
|
||||
func RplNotice(source Identifier, target Identifier, message string) string {
|
||||
func RplNotice(source Identifier, target Identifier, message Text) string {
|
||||
return NewStringReply(source, NOTICE, "%s :%s", target.Nick(), message)
|
||||
}
|
||||
|
||||
func RplNick(source Identifier, newNick string) string {
|
||||
return NewStringReply(source, NICK, newNick)
|
||||
func RplNick(source Identifier, newNick Name) string {
|
||||
return NewStringReply(source, NICK, newNick.String())
|
||||
}
|
||||
|
||||
func RplJoin(client *Client, channel *Channel) string {
|
||||
return NewStringReply(client, JOIN, channel.name)
|
||||
return NewStringReply(client, JOIN, channel.name.String())
|
||||
}
|
||||
|
||||
func RplPart(client *Client, channel *Channel, message string) string {
|
||||
func RplPart(client *Client, channel *Channel, message Text) string {
|
||||
return NewStringReply(client, PART, "%s :%s", channel, message)
|
||||
}
|
||||
|
||||
@ -117,10 +117,10 @@ func RplPing(target Identifier) string {
|
||||
}
|
||||
|
||||
func RplPong(client *Client) string {
|
||||
return NewStringReply(nil, PONG, client.Nick())
|
||||
return NewStringReply(nil, PONG, client.Nick().String())
|
||||
}
|
||||
|
||||
func RplQuit(client *Client, message string) string {
|
||||
func RplQuit(client *Client, message Text) string {
|
||||
return NewStringReply(client, QUIT, ":%s", message)
|
||||
}
|
||||
|
||||
@ -128,16 +128,16 @@ func RplError(message string) string {
|
||||
return NewStringReply(nil, ERROR, ":%s", message)
|
||||
}
|
||||
|
||||
func RplInviteMsg(inviter *Client, invitee *Client, channel string) string {
|
||||
func RplInviteMsg(inviter *Client, invitee *Client, channel Name) string {
|
||||
return NewStringReply(inviter, INVITE, "%s :%s", invitee.Nick(), channel)
|
||||
}
|
||||
|
||||
func RplKick(channel *Channel, client *Client, target *Client, comment string) string {
|
||||
func RplKick(channel *Channel, client *Client, target *Client, comment Text) string {
|
||||
return NewStringReply(client, KICK, "%s %s :%s",
|
||||
channel, target.Nick(), comment)
|
||||
}
|
||||
|
||||
func RplKill(client *Client, target *Client, comment string) string {
|
||||
func RplKill(client *Client, target *Client, comment Text) string {
|
||||
return NewStringReply(client, KICK,
|
||||
"%s :%s", target.Nick(), comment)
|
||||
}
|
||||
@ -184,7 +184,7 @@ func (target *Client) RplTopic(channel *Channel) {
|
||||
|
||||
// <nick> <channel>
|
||||
// NB: correction in errata
|
||||
func (target *Client) RplInvitingMsg(invitee *Client, channel string) {
|
||||
func (target *Client) RplInvitingMsg(invitee *Client, channel Name) {
|
||||
target.NumericReply(RPL_INVITING,
|
||||
"%s %s", invitee.Nick(), channel)
|
||||
}
|
||||
@ -253,7 +253,7 @@ func (target *Client) RplWhoReply(channel *Channel, client *Client) {
|
||||
}
|
||||
|
||||
if channel != nil {
|
||||
channelName = channel.name
|
||||
channelName = channel.name.String()
|
||||
if target.capabilities[MultiPrefix] {
|
||||
if channel.members[client][ChannelOperator] {
|
||||
flags += "@"
|
||||
@ -275,12 +275,12 @@ func (target *Client) RplWhoReply(channel *Channel, client *Client) {
|
||||
}
|
||||
|
||||
// <name> :End of WHO list
|
||||
func (target *Client) RplEndOfWho(name string) {
|
||||
func (target *Client) RplEndOfWho(name Name) {
|
||||
target.NumericReply(RPL_ENDOFWHO,
|
||||
"%s :End of WHO list", name)
|
||||
}
|
||||
|
||||
func (target *Client) RplMaskList(mode ChannelMode, channel *Channel, mask string) {
|
||||
func (target *Client) RplMaskList(mode ChannelMode, channel *Channel, mask Name) {
|
||||
switch mode {
|
||||
case BanMask:
|
||||
target.RplBanList(channel, mask)
|
||||
@ -306,7 +306,7 @@ func (target *Client) RplEndOfMaskList(mode ChannelMode, channel *Channel) {
|
||||
}
|
||||
}
|
||||
|
||||
func (target *Client) RplBanList(channel *Channel, mask string) {
|
||||
func (target *Client) RplBanList(channel *Channel, mask Name) {
|
||||
target.NumericReply(RPL_BANLIST,
|
||||
"%s %s", channel, mask)
|
||||
}
|
||||
@ -316,7 +316,7 @@ func (target *Client) RplEndOfBanList(channel *Channel) {
|
||||
"%s :End of channel ban list", channel)
|
||||
}
|
||||
|
||||
func (target *Client) RplExceptList(channel *Channel, mask string) {
|
||||
func (target *Client) RplExceptList(channel *Channel, mask Name) {
|
||||
target.NumericReply(RPL_EXCEPTLIST,
|
||||
"%s %s", channel, mask)
|
||||
}
|
||||
@ -326,7 +326,7 @@ func (target *Client) RplEndOfExceptList(channel *Channel) {
|
||||
"%s :End of channel exception list", channel)
|
||||
}
|
||||
|
||||
func (target *Client) RplInviteList(channel *Channel, mask string) {
|
||||
func (target *Client) RplInviteList(channel *Channel, mask Name) {
|
||||
target.NumericReply(RPL_INVITELIST,
|
||||
"%s %s", channel, mask)
|
||||
}
|
||||
@ -396,7 +396,7 @@ func (target *Client) RplVersion() {
|
||||
"%s %s", SEM_VER, target.server.name)
|
||||
}
|
||||
|
||||
func (target *Client) RplInviting(invitee *Client, channel string) {
|
||||
func (target *Client) RplInviting(invitee *Client, channel Name) {
|
||||
target.NumericReply(RPL_INVITING,
|
||||
"%s %s", invitee.Nick(), channel)
|
||||
}
|
||||
@ -412,7 +412,7 @@ func (target *Client) RplWhoWasUser(whoWas *WhoWas) {
|
||||
whoWas.nickname, whoWas.username, whoWas.hostname, whoWas.realname)
|
||||
}
|
||||
|
||||
func (target *Client) RplEndOfWhoWas(nickname string) {
|
||||
func (target *Client) RplEndOfWhoWas(nickname Name) {
|
||||
target.NumericReply(RPL_ENDOFWHOWAS,
|
||||
"%s :End of WHOWAS", nickname)
|
||||
}
|
||||
@ -426,7 +426,7 @@ func (target *Client) ErrAlreadyRegistered() {
|
||||
":You may not reregister")
|
||||
}
|
||||
|
||||
func (target *Client) ErrNickNameInUse(nick string) {
|
||||
func (target *Client) ErrNickNameInUse(nick Name) {
|
||||
target.NumericReply(ERR_NICKNAMEINUSE,
|
||||
"%s :Nickname is already in use", nick)
|
||||
}
|
||||
@ -441,12 +441,12 @@ func (target *Client) ErrUsersDontMatch() {
|
||||
":Cannot change mode for other users")
|
||||
}
|
||||
|
||||
func (target *Client) ErrNeedMoreParams(command string) {
|
||||
func (target *Client) ErrNeedMoreParams(command StringCode) {
|
||||
target.NumericReply(ERR_NEEDMOREPARAMS,
|
||||
"%s :Not enough parameters", command)
|
||||
}
|
||||
|
||||
func (target *Client) ErrNoSuchChannel(channel string) {
|
||||
func (target *Client) ErrNoSuchChannel(channel Name) {
|
||||
target.NumericReply(ERR_NOSUCHCHANNEL,
|
||||
"%s :No such channel", channel)
|
||||
}
|
||||
@ -471,7 +471,7 @@ func (target *Client) ErrBadChannelKey(channel *Channel) {
|
||||
"%s :Cannot join channel (+k)", channel.name)
|
||||
}
|
||||
|
||||
func (target *Client) ErrNoSuchNick(nick string) {
|
||||
func (target *Client) ErrNoSuchNick(nick Name) {
|
||||
target.NumericReply(ERR_NOSUCHNICK,
|
||||
"%s :No such nick/channel", nick)
|
||||
}
|
||||
@ -493,7 +493,7 @@ func (target *Client) ErrRestricted() {
|
||||
target.NumericReply(ERR_RESTRICTED, ":Your connection is restricted!")
|
||||
}
|
||||
|
||||
func (target *Client) ErrNoSuchServer(server string) {
|
||||
func (target *Client) ErrNoSuchServer(server Name) {
|
||||
target.NumericReply(ERR_NOSUCHSERVER, "%s :No such server", server)
|
||||
}
|
||||
|
||||
@ -521,7 +521,7 @@ func (target *Client) ErrNoNicknameGiven() {
|
||||
target.NumericReply(ERR_NONICKNAMEGIVEN, ":No nickname given")
|
||||
}
|
||||
|
||||
func (target *Client) ErrErroneusNickname(nick string) {
|
||||
func (target *Client) ErrErroneusNickname(nick Name) {
|
||||
target.NumericReply(ERR_ERRONEUSNICKNAME,
|
||||
"%s :Erroneous nickname", nick)
|
||||
}
|
||||
@ -536,7 +536,7 @@ func (target *Client) ErrChannelIsFull(channel *Channel) {
|
||||
"%s :Cannot join channel (+l)", channel)
|
||||
}
|
||||
|
||||
func (target *Client) ErrWasNoSuchNick(nickname string) {
|
||||
func (target *Client) ErrWasNoSuchNick(nickname Name) {
|
||||
target.NumericReply(ERR_WASNOSUCHNICK,
|
||||
"%s :There was no such nickname", nickname)
|
||||
}
|
||||
|
@ -24,9 +24,9 @@ type Server struct {
|
||||
db *sql.DB
|
||||
idle chan *Client
|
||||
motdFile string
|
||||
name string
|
||||
name Name
|
||||
newConns chan net.Conn
|
||||
operators map[string][]byte
|
||||
operators map[Name][]byte
|
||||
password []byte
|
||||
signals chan os.Signal
|
||||
whoWas *WhoWasList
|
||||
@ -41,7 +41,7 @@ func NewServer(config *Config) *Server {
|
||||
db: OpenDB(config.Server.Database),
|
||||
idle: make(chan *Client, 16),
|
||||
motdFile: config.Server.MOTD,
|
||||
name: config.Server.Name,
|
||||
name: NewName(config.Server.Name),
|
||||
newConns: make(chan net.Conn, 16),
|
||||
operators: config.Operators(),
|
||||
signals: make(chan os.Signal, 1),
|
||||
@ -68,7 +68,7 @@ func loadChannelList(channel *Channel, list string, maskMode ChannelMode) {
|
||||
if list == "" {
|
||||
return
|
||||
}
|
||||
channel.lists[maskMode].AddAll(strings.Split(list, " "))
|
||||
channel.lists[maskMode].AddAll(NewNames(strings.Split(list, " ")))
|
||||
}
|
||||
|
||||
func (server *Server) loadChannels() {
|
||||
@ -80,7 +80,9 @@ func (server *Server) loadChannels() {
|
||||
log.Fatal("error loading channels: ", err)
|
||||
}
|
||||
for rows.Next() {
|
||||
var name, flags, key, topic string
|
||||
var name Name
|
||||
var flags string
|
||||
var key, topic Text
|
||||
var userLimit uint64
|
||||
var banList, exceptList, inviteList string
|
||||
err = rows.Scan(&name, &flags, &key, &topic, &userLimit, &banList,
|
||||
@ -248,15 +250,15 @@ func (server *Server) MOTD(client *Client) {
|
||||
client.RplMOTDEnd()
|
||||
}
|
||||
|
||||
func (s *Server) Id() string {
|
||||
func (s *Server) Id() Name {
|
||||
return s.name
|
||||
}
|
||||
|
||||
func (s *Server) String() string {
|
||||
return s.name
|
||||
return s.name.String()
|
||||
}
|
||||
|
||||
func (s *Server) Nick() string {
|
||||
func (s *Server) Nick() Name {
|
||||
return s.Id()
|
||||
}
|
||||
|
||||
@ -339,7 +341,7 @@ func (m *NickCommand) HandleRegServer(s *Server) {
|
||||
return
|
||||
}
|
||||
|
||||
if !IsNickname(m.nickname) {
|
||||
if !m.nickname.IsNickname() {
|
||||
client.ErrErroneusNickname(m.nickname)
|
||||
return
|
||||
}
|
||||
@ -416,7 +418,7 @@ func (msg *NickCommand) HandleServer(server *Server) {
|
||||
return
|
||||
}
|
||||
|
||||
if !IsNickname(msg.nickname) {
|
||||
if !msg.nickname.IsNickname() {
|
||||
client.ErrErroneusNickname(msg.nickname)
|
||||
return
|
||||
}
|
||||
@ -447,13 +449,13 @@ func (m *JoinCommand) HandleServer(s *Server) {
|
||||
|
||||
if m.zero {
|
||||
for channel := range client.channels {
|
||||
channel.Part(client, client.Nick())
|
||||
channel.Part(client, client.Nick().Text())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
for name, key := range m.channels {
|
||||
if !IsChannel(name) {
|
||||
if !name.IsChannel() {
|
||||
client.ErrNoSuchChannel(name)
|
||||
continue
|
||||
}
|
||||
@ -497,7 +499,7 @@ func (msg *TopicCommand) HandleServer(server *Server) {
|
||||
|
||||
func (msg *PrivMsgCommand) HandleServer(server *Server) {
|
||||
client := msg.Client()
|
||||
if IsChannel(msg.target) {
|
||||
if msg.target.IsChannel() {
|
||||
channel := server.channels.Get(msg.target)
|
||||
if channel == nil {
|
||||
client.ErrNoSuchChannel(msg.target)
|
||||
@ -577,13 +579,13 @@ func (client *Client) WhoisChannelsNames() []string {
|
||||
for channel := range client.channels {
|
||||
switch {
|
||||
case channel.members[client][ChannelOperator]:
|
||||
chstrs[index] = "@" + channel.name
|
||||
chstrs[index] = "@" + channel.name.String()
|
||||
|
||||
case channel.members[client][Voice]:
|
||||
chstrs[index] = "+" + channel.name
|
||||
chstrs[index] = "+" + channel.name.String()
|
||||
|
||||
default:
|
||||
chstrs[index] = channel.name
|
||||
chstrs[index] = channel.name.String()
|
||||
}
|
||||
index += 1
|
||||
}
|
||||
@ -635,7 +637,7 @@ func (msg *WhoCommand) HandleServer(server *Server) {
|
||||
for _, channel := range server.channels {
|
||||
whoChannel(client, channel, friends)
|
||||
}
|
||||
} else if IsChannel(mask) {
|
||||
} else if mask.IsChannel() {
|
||||
// TODO implement wildcard matching
|
||||
channel := server.channels.Get(mask)
|
||||
if channel != nil {
|
||||
@ -685,7 +687,7 @@ func (msg *IsOnCommand) HandleServer(server *Server) {
|
||||
ison := make([]string, 0)
|
||||
for _, nick := range msg.nicks {
|
||||
if iclient := server.clients.Get(nick); iclient != nil {
|
||||
ison = append(ison, iclient.Nick())
|
||||
ison = append(ison, iclient.Nick().String())
|
||||
}
|
||||
}
|
||||
|
||||
@ -698,7 +700,7 @@ func (msg *MOTDCommand) HandleServer(server *Server) {
|
||||
|
||||
func (msg *NoticeCommand) HandleServer(server *Server) {
|
||||
client := msg.Client()
|
||||
if IsChannel(msg.target) {
|
||||
if msg.target.IsChannel() {
|
||||
channel := server.channels.Get(msg.target)
|
||||
if channel == nil {
|
||||
client.ErrNoSuchChannel(msg.target)
|
||||
@ -785,7 +787,7 @@ func (msg *NamesCommand) HandleServer(server *Server) {
|
||||
}
|
||||
|
||||
func (server *Server) Reply(target *Client, format string, args ...interface{}) {
|
||||
target.Reply(RplPrivMsg(server, target, fmt.Sprintf(format, args...)))
|
||||
target.Reply(RplPrivMsg(server, target, NewText(fmt.Sprintf(format, args...))))
|
||||
}
|
||||
|
||||
func (msg *DebugCommand) HandleServer(server *Server) {
|
||||
@ -880,7 +882,7 @@ func (msg *KillCommand) HandleServer(server *Server) {
|
||||
}
|
||||
|
||||
quitMsg := fmt.Sprintf("KILLed by %s: %s", client.Nick(), msg.comment)
|
||||
target.Quit(quitMsg)
|
||||
target.Quit(NewText(quitMsg))
|
||||
}
|
||||
|
||||
func (msg *WhoWasCommand) HandleServer(server *Server) {
|
||||
|
66
irc/strings.go
Normal file
66
irc/strings.go
Normal file
@ -0,0 +1,66 @@
|
||||
package irc
|
||||
|
||||
import (
|
||||
"code.google.com/p/go.text/unicode/norm"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
// regexps
|
||||
ChannelNameExpr = regexp.MustCompile(`^[&!#+][\pL\pN]{1,63}$`)
|
||||
NicknameExpr = regexp.MustCompile("^[\\pL\\pN\\pP\\pS]{1,32}$")
|
||||
)
|
||||
|
||||
// Names are normalized and canonicalized to remove formatting marks
|
||||
// and simplify usage. They are things like hostnames and usermasks.
|
||||
type Name string
|
||||
|
||||
func NewName(str string) Name {
|
||||
return Name(norm.NFKC.String(str))
|
||||
}
|
||||
|
||||
func NewNames(strs []string) []Name {
|
||||
names := make([]Name, len(strs))
|
||||
for index, str := range strs {
|
||||
names[index] = NewName(str)
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
// tests
|
||||
|
||||
func (name Name) IsChannel() bool {
|
||||
return ChannelNameExpr.MatchString(name.String())
|
||||
}
|
||||
|
||||
func (name Name) IsNickname() bool {
|
||||
return NicknameExpr.MatchString(name.String())
|
||||
}
|
||||
|
||||
// conversions
|
||||
|
||||
func (name Name) String() string {
|
||||
return string(name)
|
||||
}
|
||||
|
||||
func (name Name) ToLower() Name {
|
||||
return Name(strings.ToLower(name.String()))
|
||||
}
|
||||
|
||||
// It's safe to coerce a Name to Text. Name is a strict subset of Text.
|
||||
func (name Name) Text() Text {
|
||||
return Text(name)
|
||||
}
|
||||
|
||||
// Text is PRIVMSG, NOTICE, or TOPIC data. It's canonicalized UTF8
|
||||
// data to simplify but keeps all formatting.
|
||||
type Text string
|
||||
|
||||
func NewText(str string) Text {
|
||||
return Text(norm.NFC.String(str))
|
||||
}
|
||||
|
||||
func (text Text) String() string {
|
||||
return string(text)
|
||||
}
|
20
irc/types.go
20
irc/types.go
@ -67,7 +67,7 @@ type ReplyCode interface {
|
||||
String() string
|
||||
}
|
||||
|
||||
type StringCode string
|
||||
type StringCode Name
|
||||
|
||||
func (code StringCode) String() string {
|
||||
return string(code)
|
||||
@ -86,25 +86,25 @@ func (mode ChannelMode) String() string {
|
||||
return string(mode)
|
||||
}
|
||||
|
||||
type ChannelNameMap map[string]*Channel
|
||||
type ChannelNameMap map[Name]*Channel
|
||||
|
||||
func (channels ChannelNameMap) Get(name string) *Channel {
|
||||
return channels[strings.ToLower(name)]
|
||||
func (channels ChannelNameMap) Get(name Name) *Channel {
|
||||
return channels[name.ToLower()]
|
||||
}
|
||||
|
||||
func (channels ChannelNameMap) Add(channel *Channel) error {
|
||||
if channels[channel.name] != nil {
|
||||
if channels[channel.name.ToLower()] != nil {
|
||||
return fmt.Errorf("%s: already set", channel.name)
|
||||
}
|
||||
channels[channel.name] = channel
|
||||
channels[channel.name.ToLower()] = channel
|
||||
return nil
|
||||
}
|
||||
|
||||
func (channels ChannelNameMap) Remove(channel *Channel) error {
|
||||
if channel != channels[channel.name] {
|
||||
if channel != channels[channel.name.ToLower()] {
|
||||
return fmt.Errorf("%s: mismatch", channel.name)
|
||||
}
|
||||
delete(channels, channel.name)
|
||||
delete(channels, channel.name.ToLower())
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -182,8 +182,8 @@ func (channels ChannelSet) First() *Channel {
|
||||
//
|
||||
|
||||
type Identifier interface {
|
||||
Id() string
|
||||
Nick() string
|
||||
Id() Name
|
||||
Nick() Name
|
||||
}
|
||||
|
||||
type Replier interface {
|
||||
|
@ -7,10 +7,10 @@ type WhoWasList struct {
|
||||
}
|
||||
|
||||
type WhoWas struct {
|
||||
nickname string
|
||||
username string
|
||||
hostname string
|
||||
realname string
|
||||
nickname Name
|
||||
username Name
|
||||
hostname Name
|
||||
realname Text
|
||||
}
|
||||
|
||||
func NewWhoWasList(size uint) *WhoWasList {
|
||||
@ -32,7 +32,7 @@ func (list *WhoWasList) Append(client *Client) {
|
||||
}
|
||||
}
|
||||
|
||||
func (list *WhoWasList) Find(nickname string, limit int64) []*WhoWas {
|
||||
func (list *WhoWasList) Find(nickname Name, limit int64) []*WhoWas {
|
||||
results := make([]*WhoWas, 0)
|
||||
for whoWas := range list.Each() {
|
||||
if nickname != whoWas.nickname {
|
||||
|
Loading…
Reference in New Issue
Block a user