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