Преглед на файлове

mark unicode normalization with type

tags/v0.1.0
Jeremy Latt преди 10 години
родител
ревизия
96a108f8da
променени са 12 файла, в които са добавени 324 реда и са изтрити 269 реда
  1. 26
    28
      irc/channel.go
  2. 15
    19
      irc/client.go
  3. 24
    24
      irc/client_lookup_set.go
  4. 117
    118
      irc/commands.go
  5. 3
    3
      irc/config.go
  6. 0
    6
      irc/constants.go
  7. 8
    8
      irc/net.go
  8. 27
    27
      irc/reply.go
  9. 23
    21
      irc/server.go
  10. 66
    0
      irc/strings.go
  11. 10
    10
      irc/types.go
  12. 5
    5
      irc/whowas.go

+ 26
- 28
irc/channel.go Целия файл

3
 import (
3
 import (
4
 	"log"
4
 	"log"
5
 	"strconv"
5
 	"strconv"
6
-	"strings"
7
 )
6
 )
8
 
7
 
9
 type Channel struct {
8
 type Channel struct {
10
 	flags     ChannelModeSet
9
 	flags     ChannelModeSet
11
 	lists     map[ChannelMode]*UserMaskSet
10
 	lists     map[ChannelMode]*UserMaskSet
12
-	key       string
11
+	key       Text
13
 	members   MemberSet
12
 	members   MemberSet
14
-	name      string
13
+	name      Name
15
 	server    *Server
14
 	server    *Server
16
-	topic     string
15
+	topic     Text
17
 	userLimit uint64
16
 	userLimit uint64
18
 }
17
 }
19
 
18
 
20
-func IsChannel(target string) bool {
21
-	return ChannelNameExpr.MatchString(target)
22
-}
23
-
24
 // NewChannel creates a new channel from a `Server` and a `name`
19
 // NewChannel creates a new channel from a `Server` and a `name`
25
 // string, which must be unique on the server.
20
 // string, which must be unique on the server.
26
-func NewChannel(s *Server, name string) *Channel {
21
+func NewChannel(s *Server, name Name) *Channel {
27
 	channel := &Channel{
22
 	channel := &Channel{
28
 		flags: make(ChannelModeSet),
23
 		flags: make(ChannelModeSet),
29
 		lists: map[ChannelMode]*UserMaskSet{
24
 		lists: map[ChannelMode]*UserMaskSet{
32
 			InviteMask: NewUserMaskSet(),
27
 			InviteMask: NewUserMaskSet(),
33
 		},
28
 		},
34
 		members: make(MemberSet),
29
 		members: make(MemberSet),
35
-		name:    strings.ToLower(name),
30
+		name:    name,
36
 		server:  s,
31
 		server:  s,
37
 	}
32
 	}
38
 
33
 
73
 				nicks[i] += "+"
68
 				nicks[i] += "+"
74
 			}
69
 			}
75
 		}
70
 		}
76
-		nicks[i] += client.Nick()
71
+		nicks[i] += client.Nick().String()
77
 		i += 1
72
 		i += 1
78
 	}
73
 	}
79
 	return nicks
74
 	return nicks
80
 }
75
 }
81
 
76
 
82
-func (channel *Channel) Id() string {
77
+func (channel *Channel) Id() Name {
83
 	return channel.name
78
 	return channel.name
84
 }
79
 }
85
 
80
 
86
-func (channel *Channel) Nick() string {
81
+func (channel *Channel) Nick() Name {
87
 	return channel.name
82
 	return channel.name
88
 }
83
 }
89
 
84
 
90
 func (channel *Channel) String() string {
85
 func (channel *Channel) String() string {
91
-	return channel.Id()
86
+	return channel.Id().String()
92
 }
87
 }
93
 
88
 
94
 // <mode> <mode params>
89
 // <mode> <mode params>
117
 	// args for flags with args: The order must match above to keep
112
 	// args for flags with args: The order must match above to keep
118
 	// positional arguments in place.
113
 	// positional arguments in place.
119
 	if showKey {
114
 	if showKey {
120
-		str += " " + channel.key
115
+		str += " " + channel.key.String()
121
 	}
116
 	}
122
 	if showUserLimit {
117
 	if showUserLimit {
123
 		str += " " + strconv.FormatUint(channel.userLimit, 10)
118
 		str += " " + strconv.FormatUint(channel.userLimit, 10)
131
 		(uint64(len(channel.members)) >= channel.userLimit)
126
 		(uint64(len(channel.members)) >= channel.userLimit)
132
 }
127
 }
133
 
128
 
134
-func (channel *Channel) CheckKey(key string) bool {
129
+func (channel *Channel) CheckKey(key Text) bool {
135
 	return (channel.key == "") || (channel.key == key)
130
 	return (channel.key == "") || (channel.key == key)
136
 }
131
 }
137
 
132
 
138
-func (channel *Channel) Join(client *Client, key string) {
133
+func (channel *Channel) Join(client *Client, key Text) {
139
 	if channel.members.Has(client) {
134
 	if channel.members.Has(client) {
140
 		// already joined, no message?
135
 		// already joined, no message?
141
 		return
136
 		return
179
 	channel.Names(client)
174
 	channel.Names(client)
180
 }
175
 }
181
 
176
 
182
-func (channel *Channel) Part(client *Client, message string) {
177
+func (channel *Channel) Part(client *Client, message Text) {
183
 	if !channel.members.Has(client) {
178
 	if !channel.members.Has(client) {
184
 		client.ErrNotOnChannel(channel)
179
 		client.ErrNotOnChannel(channel)
185
 		return
180
 		return
207
 	client.RplTopic(channel)
202
 	client.RplTopic(channel)
208
 }
203
 }
209
 
204
 
210
-func (channel *Channel) SetTopic(client *Client, topic string) {
205
+func (channel *Channel) SetTopic(client *Client, topic Text) {
211
 	if !(client.flags[Operator] || channel.members.Has(client)) {
206
 	if !(client.flags[Operator] || channel.members.Has(client)) {
212
 		client.ErrNotOnChannel(channel)
207
 		client.ErrNotOnChannel(channel)
213
 		return
208
 		return
244
 	return true
239
 	return true
245
 }
240
 }
246
 
241
 
247
-func (channel *Channel) PrivMsg(client *Client, message string) {
242
+func (channel *Channel) PrivMsg(client *Client, message Text) {
248
 	if !channel.CanSpeak(client) {
243
 	if !channel.CanSpeak(client) {
249
 		client.ErrCannotSendToChan(channel)
244
 		client.ErrCannotSendToChan(channel)
250
 		return
245
 		return
283
 }
278
 }
284
 
279
 
285
 func (channel *Channel) applyModeMember(client *Client, mode ChannelMode,
280
 func (channel *Channel) applyModeMember(client *Client, mode ChannelMode,
286
-	op ModeOp, nick string) bool {
281
+	op ModeOp, nick Name) bool {
287
 	if !channel.ClientIsOperator(client) {
282
 	if !channel.ClientIsOperator(client) {
288
 		client.ErrChanOPrivIsNeeded(channel)
283
 		client.ErrChanOPrivIsNeeded(channel)
289
 		return false
284
 		return false
331
 }
326
 }
332
 
327
 
333
 func (channel *Channel) applyModeMask(client *Client, mode ChannelMode, op ModeOp,
328
 func (channel *Channel) applyModeMask(client *Client, mode ChannelMode, op ModeOp,
334
-	mask string) bool {
329
+	mask Name) bool {
335
 	list := channel.lists[mode]
330
 	list := channel.lists[mode]
336
 	if list == nil {
331
 	if list == nil {
337
 		// This should never happen, but better safe than panicky.
332
 		// This should never happen, but better safe than panicky.
362
 func (channel *Channel) applyMode(client *Client, change *ChannelModeChange) bool {
357
 func (channel *Channel) applyMode(client *Client, change *ChannelModeChange) bool {
363
 	switch change.mode {
358
 	switch change.mode {
364
 	case BanMask, ExceptMask, InviteMask:
359
 	case BanMask, ExceptMask, InviteMask:
365
-		return channel.applyModeMask(client, change.mode, change.op, change.arg)
360
+		return channel.applyModeMask(client, change.mode, change.op,
361
+			NewName(change.arg))
366
 
362
 
367
 	case InviteOnly, Moderated, NoOutside, OpOnlyTopic, Persistent, Private:
363
 	case InviteOnly, Moderated, NoOutside, OpOnlyTopic, Persistent, Private:
368
 		return channel.applyModeFlag(client, change.mode, change.op)
364
 		return channel.applyModeFlag(client, change.mode, change.op)
379
 				client.ErrNeedMoreParams("MODE")
375
 				client.ErrNeedMoreParams("MODE")
380
 				return false
376
 				return false
381
 			}
377
 			}
382
-			if change.arg == channel.key {
378
+			key := NewText(change.arg)
379
+			if key == channel.key {
383
 				return false
380
 				return false
384
 			}
381
 			}
385
 
382
 
386
-			channel.key = change.arg
383
+			channel.key = key
387
 			return true
384
 			return true
388
 
385
 
389
 		case Remove:
386
 		case Remove:
405
 		return true
402
 		return true
406
 
403
 
407
 	case ChannelOperator, Voice:
404
 	case ChannelOperator, Voice:
408
-		return channel.applyModeMember(client, change.mode, change.op, change.arg)
405
+		return channel.applyModeMember(client, change.mode, change.op,
406
+			NewName(change.arg))
409
 
407
 
410
 	default:
408
 	default:
411
 		client.ErrUnknownMode(change.mode, channel)
409
 		client.ErrUnknownMode(change.mode, channel)
456
 	return
454
 	return
457
 }
455
 }
458
 
456
 
459
-func (channel *Channel) Notice(client *Client, message string) {
457
+func (channel *Channel) Notice(client *Client, message Text) {
460
 	if !channel.CanSpeak(client) {
458
 	if !channel.CanSpeak(client) {
461
 		client.ErrCannotSendToChan(channel)
459
 		client.ErrCannotSendToChan(channel)
462
 		return
460
 		return
478
 	}
476
 	}
479
 }
477
 }
480
 
478
 
481
-func (channel *Channel) Kick(client *Client, target *Client, comment string) {
479
+func (channel *Channel) Kick(client *Client, target *Client, comment Text) {
482
 	if !(client.flags[Operator] || channel.members.Has(client)) {
480
 	if !(client.flags[Operator] || channel.members.Has(client)) {
483
 		client.ErrNotOnChannel(channel)
481
 		client.ErrNotOnChannel(channel)
484
 		return
482
 		return

+ 15
- 19
irc/client.go Целия файл

7
 	"time"
7
 	"time"
8
 )
8
 )
9
 
9
 
10
-func IsNickname(nick string) bool {
11
-	return NicknameExpr.MatchString(nick)
12
-}
13
-
14
 type Client struct {
10
 type Client struct {
15
 	atime        time.Time
11
 	atime        time.Time
16
 	authorized   bool
12
 	authorized   bool
17
-	awayMessage  string
13
+	awayMessage  Text
18
 	capabilities CapabilitySet
14
 	capabilities CapabilitySet
19
 	capState     CapState
15
 	capState     CapState
20
 	channels     ChannelSet
16
 	channels     ChannelSet
23
 	flags        map[UserMode]bool
19
 	flags        map[UserMode]bool
24
 	hasQuit      bool
20
 	hasQuit      bool
25
 	hops         uint
21
 	hops         uint
26
-	hostname     string
22
+	hostname     Name
27
 	idleTimer    *time.Timer
23
 	idleTimer    *time.Timer
28
 	loginTimer   *time.Timer
24
 	loginTimer   *time.Timer
29
-	nick         string
25
+	nick         Name
30
 	phase        Phase
26
 	phase        Phase
31
 	quitTimer    *time.Timer
27
 	quitTimer    *time.Timer
32
-	realname     string
28
+	realname     Text
33
 	server       *Server
29
 	server       *Server
34
 	socket       *Socket
30
 	socket       *Socket
35
-	username     string
31
+	username     Name
36
 }
32
 }
37
 
33
 
38
 func NewClient(server *Server, conn net.Conn) *Client {
34
 func NewClient(server *Server, conn net.Conn) *Client {
186
 	return
182
 	return
187
 }
183
 }
188
 
184
 
189
-func (c *Client) UserHost() string {
185
+func (c *Client) UserHost() Name {
190
 	username := "*"
186
 	username := "*"
191
 	if c.HasUsername() {
187
 	if c.HasUsername() {
192
-		username = c.username
188
+		username = c.username.String()
193
 	}
189
 	}
194
-	return fmt.Sprintf("%s!%s@%s", c.Nick(), username, c.hostname)
190
+	return Name(fmt.Sprintf("%s!%s@%s", c.Nick(), username, c.hostname))
195
 }
191
 }
196
 
192
 
197
-func (c *Client) Nick() string {
193
+func (c *Client) Nick() Name {
198
 	if c.HasNick() {
194
 	if c.HasNick() {
199
 		return c.nick
195
 		return c.nick
200
 	}
196
 	}
201
-	return "*"
197
+	return Name("*")
202
 }
198
 }
203
 
199
 
204
-func (c *Client) Id() string {
200
+func (c *Client) Id() Name {
205
 	return c.UserHost()
201
 	return c.UserHost()
206
 }
202
 }
207
 
203
 
208
 func (c *Client) String() string {
204
 func (c *Client) String() string {
209
-	return c.Id()
205
+	return c.Id().String()
210
 }
206
 }
211
 
207
 
212
 func (client *Client) Friends() ClientSet {
208
 func (client *Client) Friends() ClientSet {
220
 	return friends
216
 	return friends
221
 }
217
 }
222
 
218
 
223
-func (client *Client) SetNickname(nickname string) {
219
+func (client *Client) SetNickname(nickname Name) {
224
 	client.nick = nickname
220
 	client.nick = nickname
225
 	client.server.clients.Add(client)
221
 	client.server.clients.Add(client)
226
 }
222
 }
227
 
223
 
228
-func (client *Client) ChangeNickname(nickname string) {
224
+func (client *Client) ChangeNickname(nickname Name) {
229
 	// Make reply before changing nick to capture original source id.
225
 	// Make reply before changing nick to capture original source id.
230
 	reply := RplNick(client, nickname)
226
 	reply := RplNick(client, nickname)
231
 	client.server.clients.Remove(client)
227
 	client.server.clients.Remove(client)
244
 	client.socket.Write(reply)
240
 	client.socket.Write(reply)
245
 }
241
 }
246
 
242
 
247
-func (client *Client) Quit(message string) {
243
+func (client *Client) Quit(message Text) {
248
 	if client.hasQuit {
244
 	if client.hasQuit {
249
 		return
245
 		return
250
 	}
246
 	}

+ 24
- 24
irc/client_lookup_set.go Целия файл

25
 	return wildMaskExpr.MatchString(mask)
25
 	return wildMaskExpr.MatchString(mask)
26
 }
26
 }
27
 
27
 
28
-func ExpandUserHost(userhost string) (expanded string) {
28
+func ExpandUserHost(userhost Name) (expanded Name) {
29
 	expanded = userhost
29
 	expanded = userhost
30
 	// fill in missing wildcards for nicks
30
 	// fill in missing wildcards for nicks
31
-	if !strings.Contains(expanded, "!") {
31
+	if !strings.Contains(expanded.String(), "!") {
32
 		expanded += "!*"
32
 		expanded += "!*"
33
 	}
33
 	}
34
-	if !strings.Contains(expanded, "@") {
34
+	if !strings.Contains(expanded.String(), "@") {
35
 		expanded += "@*"
35
 		expanded += "@*"
36
 	}
36
 	}
37
 	return
37
 	return
38
 }
38
 }
39
 
39
 
40
-func QuoteLike(userhost string) string {
41
-	return likeQuoter.Replace(userhost)
40
+func QuoteLike(userhost Name) Name {
41
+	return Name(likeQuoter.Replace(userhost.String()))
42
 }
42
 }
43
 
43
 
44
 type ClientLookupSet struct {
44
 type ClientLookupSet struct {
45
-	byNick map[string]*Client
45
+	byNick map[Name]*Client
46
 	db     *ClientDB
46
 	db     *ClientDB
47
 }
47
 }
48
 
48
 
49
 func NewClientLookupSet() *ClientLookupSet {
49
 func NewClientLookupSet() *ClientLookupSet {
50
 	return &ClientLookupSet{
50
 	return &ClientLookupSet{
51
-		byNick: make(map[string]*Client),
51
+		byNick: make(map[Name]*Client),
52
 		db:     NewClientDB(),
52
 		db:     NewClientDB(),
53
 	}
53
 	}
54
 }
54
 }
55
 
55
 
56
-func (clients *ClientLookupSet) Get(nick string) *Client {
57
-	return clients.byNick[strings.ToLower(nick)]
56
+func (clients *ClientLookupSet) Get(nick Name) *Client {
57
+	return clients.byNick[nick.ToLower()]
58
 }
58
 }
59
 
59
 
60
 func (clients *ClientLookupSet) Add(client *Client) error {
60
 func (clients *ClientLookupSet) Add(client *Client) error {
64
 	if clients.Get(client.nick) != nil {
64
 	if clients.Get(client.nick) != nil {
65
 		return ErrNicknameInUse
65
 		return ErrNicknameInUse
66
 	}
66
 	}
67
-	clients.byNick[strings.ToLower(client.nick)] = client
67
+	clients.byNick[client.Nick().ToLower()] = client
68
 	clients.db.Add(client)
68
 	clients.db.Add(client)
69
 	return nil
69
 	return nil
70
 }
70
 }
76
 	if clients.Get(client.nick) != client {
76
 	if clients.Get(client.nick) != client {
77
 		return ErrNicknameMismatch
77
 		return ErrNicknameMismatch
78
 	}
78
 	}
79
-	delete(clients.byNick, strings.ToLower(client.nick))
79
+	delete(clients.byNick, client.nick.ToLower())
80
 	clients.db.Remove(client)
80
 	clients.db.Remove(client)
81
 	return nil
81
 	return nil
82
 }
82
 }
83
 
83
 
84
-func (clients *ClientLookupSet) FindAll(userhost string) (set ClientSet) {
84
+func (clients *ClientLookupSet) FindAll(userhost Name) (set ClientSet) {
85
 	userhost = ExpandUserHost(userhost)
85
 	userhost = ExpandUserHost(userhost)
86
 	set = make(ClientSet)
86
 	set = make(ClientSet)
87
 	rows, err := clients.db.db.Query(
87
 	rows, err := clients.db.db.Query(
94
 		return
94
 		return
95
 	}
95
 	}
96
 	for rows.Next() {
96
 	for rows.Next() {
97
-		var nickname string
97
+		var nickname Name
98
 		err := rows.Scan(&nickname)
98
 		err := rows.Scan(&nickname)
99
 		if err != nil {
99
 		if err != nil {
100
 			if DEBUG_SERVER {
100
 			if DEBUG_SERVER {
114
 	return
114
 	return
115
 }
115
 }
116
 
116
 
117
-func (clients *ClientLookupSet) Find(userhost string) *Client {
117
+func (clients *ClientLookupSet) Find(userhost Name) *Client {
118
 	userhost = ExpandUserHost(userhost)
118
 	userhost = ExpandUserHost(userhost)
119
 	row := clients.db.db.QueryRow(
119
 	row := clients.db.db.QueryRow(
120
 		`SELECT nickname FROM client WHERE userhost LIKE ? ESCAPE '\' LIMIT 1`,
120
 		`SELECT nickname FROM client WHERE userhost LIKE ? ESCAPE '\' LIMIT 1`,
121
 		QuoteLike(userhost))
121
 		QuoteLike(userhost))
122
-	var nickname string
122
+	var nickname Name
123
 	err := row.Scan(&nickname)
123
 	err := row.Scan(&nickname)
124
 	if err != nil {
124
 	if err != nil {
125
 		if DEBUG_SERVER {
125
 		if DEBUG_SERVER {
184
 //
184
 //
185
 
185
 
186
 type UserMaskSet struct {
186
 type UserMaskSet struct {
187
-	masks  map[string]bool
187
+	masks  map[Name]bool
188
 	regexp *regexp.Regexp
188
 	regexp *regexp.Regexp
189
 }
189
 }
190
 
190
 
191
 func NewUserMaskSet() *UserMaskSet {
191
 func NewUserMaskSet() *UserMaskSet {
192
 	return &UserMaskSet{
192
 	return &UserMaskSet{
193
-		masks: make(map[string]bool),
193
+		masks: make(map[Name]bool),
194
 	}
194
 	}
195
 }
195
 }
196
 
196
 
197
-func (set *UserMaskSet) Add(mask string) bool {
197
+func (set *UserMaskSet) Add(mask Name) bool {
198
 	if set.masks[mask] {
198
 	if set.masks[mask] {
199
 		return false
199
 		return false
200
 	}
200
 	}
203
 	return true
203
 	return true
204
 }
204
 }
205
 
205
 
206
-func (set *UserMaskSet) AddAll(masks []string) (added bool) {
206
+func (set *UserMaskSet) AddAll(masks []Name) (added bool) {
207
 	for _, mask := range masks {
207
 	for _, mask := range masks {
208
 		if !added && !set.masks[mask] {
208
 		if !added && !set.masks[mask] {
209
 			added = true
209
 			added = true
214
 	return
214
 	return
215
 }
215
 }
216
 
216
 
217
-func (set *UserMaskSet) Remove(mask string) bool {
217
+func (set *UserMaskSet) Remove(mask Name) bool {
218
 	if !set.masks[mask] {
218
 	if !set.masks[mask] {
219
 		return false
219
 		return false
220
 	}
220
 	}
223
 	return true
223
 	return true
224
 }
224
 }
225
 
225
 
226
-func (set *UserMaskSet) Match(userhost string) bool {
226
+func (set *UserMaskSet) Match(userhost Name) bool {
227
 	if set.regexp == nil {
227
 	if set.regexp == nil {
228
 		return false
228
 		return false
229
 	}
229
 	}
230
-	return set.regexp.MatchString(userhost)
230
+	return set.regexp.MatchString(userhost.String())
231
 }
231
 }
232
 
232
 
233
 func (set *UserMaskSet) String() string {
233
 func (set *UserMaskSet) String() string {
234
 	masks := make([]string, len(set.masks))
234
 	masks := make([]string, len(set.masks))
235
 	index := 0
235
 	index := 0
236
 	for mask := range set.masks {
236
 	for mask := range set.masks {
237
-		masks[index] = mask
237
+		masks[index] = mask.String()
238
 		index += 1
238
 		index += 1
239
 	}
239
 	}
240
 	return strings.Join(masks, " ")
240
 	return strings.Join(masks, " ")
255
 	maskExprs := make([]string, len(set.masks))
255
 	maskExprs := make([]string, len(set.masks))
256
 	index := 0
256
 	index := 0
257
 	for mask := range set.masks {
257
 	for mask := range set.masks {
258
-		manyParts := strings.Split(mask, "*")
258
+		manyParts := strings.Split(mask.String(), "*")
259
 		manyExprs := make([]string, len(manyParts))
259
 		manyExprs := make([]string, len(manyParts))
260
 		for mindex, manyPart := range manyParts {
260
 		for mindex, manyPart := range manyParts {
261
 			oneParts := strings.Split(manyPart, "?")
261
 			oneParts := strings.Split(manyPart, "?")

+ 117
- 118
irc/commands.go Целия файл

1
 package irc
1
 package irc
2
 
2
 
3
 import (
3
 import (
4
-	"code.google.com/p/go.text/unicode/norm"
5
 	"errors"
4
 	"errors"
6
 	"fmt"
5
 	"fmt"
7
 	"regexp"
6
 	"regexp"
114
 		_, line = splitArg(line)
113
 		_, line = splitArg(line)
115
 	}
114
 	}
116
 	arg, line := splitArg(line)
115
 	arg, line := splitArg(line)
117
-	command = StringCode(strings.ToUpper(arg))
116
+	command = StringCode(NewName(strings.ToUpper(arg)))
118
 	for len(line) > 0 {
117
 	for len(line) > 0 {
119
 		if strings.HasPrefix(line, ":") {
118
 		if strings.HasPrefix(line, ":") {
120
-			args = append(args, norm.NFC.String(line[len(":"):]))
119
+			args = append(args, line[len(":"):])
121
 			break
120
 			break
122
 		}
121
 		}
123
 		arg, line = splitArg(line)
122
 		arg, line = splitArg(line)
124
-		args = append(args, norm.NFKC.String(arg))
123
+		args = append(args, arg)
125
 	}
124
 	}
126
 	return
125
 	return
127
 }
126
 }
147
 
146
 
148
 type PingCommand struct {
147
 type PingCommand struct {
149
 	BaseCommand
148
 	BaseCommand
150
-	server  string
151
-	server2 string
149
+	server  Name
150
+	server2 Name
152
 }
151
 }
153
 
152
 
154
 func (cmd *PingCommand) String() string {
153
 func (cmd *PingCommand) String() string {
160
 		return nil, NotEnoughArgsError
159
 		return nil, NotEnoughArgsError
161
 	}
160
 	}
162
 	msg := &PingCommand{
161
 	msg := &PingCommand{
163
-		server: args[0],
162
+		server: NewName(args[0]),
164
 	}
163
 	}
165
 	if len(args) > 1 {
164
 	if len(args) > 1 {
166
-		msg.server2 = args[1]
165
+		msg.server2 = NewName(args[1])
167
 	}
166
 	}
168
 	return msg, nil
167
 	return msg, nil
169
 }
168
 }
172
 
171
 
173
 type PongCommand struct {
172
 type PongCommand struct {
174
 	BaseCommand
173
 	BaseCommand
175
-	server1 string
176
-	server2 string
174
+	server1 Name
175
+	server2 Name
177
 }
176
 }
178
 
177
 
179
 func (cmd *PongCommand) String() string {
178
 func (cmd *PongCommand) String() string {
185
 		return nil, NotEnoughArgsError
184
 		return nil, NotEnoughArgsError
186
 	}
185
 	}
187
 	message := &PongCommand{
186
 	message := &PongCommand{
188
-		server1: args[0],
187
+		server1: NewName(args[0]),
189
 	}
188
 	}
190
 	if len(args) > 1 {
189
 	if len(args) > 1 {
191
-		message.server2 = args[1]
190
+		message.server2 = NewName(args[1])
192
 	}
191
 	}
193
 	return message, nil
192
 	return message, nil
194
 }
193
 }
230
 
229
 
231
 type NickCommand struct {
230
 type NickCommand struct {
232
 	BaseCommand
231
 	BaseCommand
233
-	nickname string
232
+	nickname Name
234
 }
233
 }
235
 
234
 
236
 func (m *NickCommand) String() string {
235
 func (m *NickCommand) String() string {
242
 		return nil, NotEnoughArgsError
241
 		return nil, NotEnoughArgsError
243
 	}
242
 	}
244
 	return &NickCommand{
243
 	return &NickCommand{
245
-		nickname: args[0],
244
+		nickname: NewName(args[0]),
246
 	}, nil
245
 	}, nil
247
 }
246
 }
248
 
247
 
249
 type UserCommand struct {
248
 type UserCommand struct {
250
 	BaseCommand
249
 	BaseCommand
251
-	username string
252
-	realname string
250
+	username Name
251
+	realname Text
253
 }
252
 }
254
 
253
 
255
 // USER <username> <hostname> <servername> <realname>
254
 // USER <username> <hostname> <servername> <realname>
256
 type RFC1459UserCommand struct {
255
 type RFC1459UserCommand struct {
257
 	UserCommand
256
 	UserCommand
258
-	hostname   string
259
-	servername string
257
+	hostname   Name
258
+	servername Name
260
 }
259
 }
261
 
260
 
262
 func (cmd *RFC1459UserCommand) String() string {
261
 func (cmd *RFC1459UserCommand) String() string {
297
 			mode:   uint8(mode),
296
 			mode:   uint8(mode),
298
 			unused: args[2],
297
 			unused: args[2],
299
 		}
298
 		}
300
-		msg.username = args[0]
301
-		msg.realname = args[3]
299
+		msg.username = NewName(args[0])
300
+		msg.realname = NewText(args[3])
302
 		return msg, nil
301
 		return msg, nil
303
 	}
302
 	}
304
 
303
 
305
 	msg := &RFC1459UserCommand{
304
 	msg := &RFC1459UserCommand{
306
-		hostname:   args[1],
307
-		servername: args[2],
305
+		hostname:   NewName(args[1]),
306
+		servername: NewName(args[2]),
308
 	}
307
 	}
309
-	msg.username = args[0]
310
-	msg.realname = args[3]
308
+	msg.username = NewName(args[0])
309
+	msg.realname = NewText(args[3])
311
 	return msg, nil
310
 	return msg, nil
312
 }
311
 }
313
 
312
 
315
 
314
 
316
 type QuitCommand struct {
315
 type QuitCommand struct {
317
 	BaseCommand
316
 	BaseCommand
318
-	message string
317
+	message Text
319
 }
318
 }
320
 
319
 
321
 func (cmd *QuitCommand) String() string {
320
 func (cmd *QuitCommand) String() string {
325
 func NewQuitCommand(args []string) (editableCommand, error) {
324
 func NewQuitCommand(args []string) (editableCommand, error) {
326
 	msg := &QuitCommand{}
325
 	msg := &QuitCommand{}
327
 	if len(args) > 0 {
326
 	if len(args) > 0 {
328
-		msg.message = args[0]
327
+		msg.message = NewText(args[0])
329
 	}
328
 	}
330
 	return msg, nil
329
 	return msg, nil
331
 }
330
 }
334
 
333
 
335
 type JoinCommand struct {
334
 type JoinCommand struct {
336
 	BaseCommand
335
 	BaseCommand
337
-	channels map[string]string
336
+	channels map[Name]Text
338
 	zero     bool
337
 	zero     bool
339
 }
338
 }
340
 
339
 
344
 
343
 
345
 func NewJoinCommand(args []string) (editableCommand, error) {
344
 func NewJoinCommand(args []string) (editableCommand, error) {
346
 	msg := &JoinCommand{
345
 	msg := &JoinCommand{
347
-		channels: make(map[string]string),
346
+		channels: make(map[Name]Text),
348
 	}
347
 	}
349
 
348
 
350
 	if len(args) == 0 {
349
 	if len(args) == 0 {
364
 		}
363
 		}
365
 	}
364
 	}
366
 	for i, channel := range channels {
365
 	for i, channel := range channels {
367
-		msg.channels[channel] = keys[i]
366
+		msg.channels[NewName(channel)] = NewText(keys[i])
368
 	}
367
 	}
369
 
368
 
370
 	return msg, nil
369
 	return msg, nil
374
 
373
 
375
 type PartCommand struct {
374
 type PartCommand struct {
376
 	BaseCommand
375
 	BaseCommand
377
-	channels []string
378
-	message  string
376
+	channels []Name
377
+	message  Text
379
 }
378
 }
380
 
379
 
381
-func (cmd *PartCommand) Message() string {
380
+func (cmd *PartCommand) Message() Text {
382
 	if cmd.message == "" {
381
 	if cmd.message == "" {
383
-		return cmd.Client().Nick()
382
+		return cmd.Client().Nick().Text()
384
 	}
383
 	}
385
 	return cmd.message
384
 	return cmd.message
386
 }
385
 }
394
 		return nil, NotEnoughArgsError
393
 		return nil, NotEnoughArgsError
395
 	}
394
 	}
396
 	msg := &PartCommand{
395
 	msg := &PartCommand{
397
-		channels: strings.Split(args[0], ","),
396
+		channels: NewNames(strings.Split(args[0], ",")),
398
 	}
397
 	}
399
 	if len(args) > 1 {
398
 	if len(args) > 1 {
400
-		msg.message = args[1]
399
+		msg.message = NewText(args[1])
401
 	}
400
 	}
402
 	return msg, nil
401
 	return msg, nil
403
 }
402
 }
406
 
405
 
407
 type PrivMsgCommand struct {
406
 type PrivMsgCommand struct {
408
 	BaseCommand
407
 	BaseCommand
409
-	target  string
410
-	message string
408
+	target  Name
409
+	message Text
411
 }
410
 }
412
 
411
 
413
 func (cmd *PrivMsgCommand) String() string {
412
 func (cmd *PrivMsgCommand) String() string {
419
 		return nil, NotEnoughArgsError
418
 		return nil, NotEnoughArgsError
420
 	}
419
 	}
421
 	return &PrivMsgCommand{
420
 	return &PrivMsgCommand{
422
-		target:  args[0],
423
-		message: args[1],
421
+		target:  NewName(args[0]),
422
+		message: NewText(args[1]),
424
 	}, nil
423
 	}, nil
425
 }
424
 }
426
 
425
 
428
 
427
 
429
 type TopicCommand struct {
428
 type TopicCommand struct {
430
 	BaseCommand
429
 	BaseCommand
431
-	channel  string
430
+	channel  Name
432
 	setTopic bool
431
 	setTopic bool
433
-	topic    string
432
+	topic    Text
434
 }
433
 }
435
 
434
 
436
 func (cmd *TopicCommand) String() string {
435
 func (cmd *TopicCommand) String() string {
442
 		return nil, NotEnoughArgsError
441
 		return nil, NotEnoughArgsError
443
 	}
442
 	}
444
 	msg := &TopicCommand{
443
 	msg := &TopicCommand{
445
-		channel: args[0],
444
+		channel: NewName(args[0]),
446
 	}
445
 	}
447
 	if len(args) > 1 {
446
 	if len(args) > 1 {
448
 		msg.setTopic = true
447
 		msg.setTopic = true
449
-		msg.topic = args[1]
448
+		msg.topic = NewText(args[1])
450
 	}
449
 	}
451
 	return msg, nil
450
 	return msg, nil
452
 }
451
 }
482
 
481
 
483
 type ModeCommand struct {
482
 type ModeCommand struct {
484
 	BaseCommand
483
 	BaseCommand
485
-	nickname string
484
+	nickname Name
486
 	changes  ModeChanges
485
 	changes  ModeChanges
487
 }
486
 }
488
 
487
 
489
 // MODE <nickname> *( ( "+" / "-" ) *( "i" / "w" / "o" / "O" / "r" ) )
488
 // MODE <nickname> *( ( "+" / "-" ) *( "i" / "w" / "o" / "O" / "r" ) )
490
-func NewUserModeCommand(args []string) (editableCommand, error) {
489
+func NewUserModeCommand(nickname Name, args []string) (editableCommand, error) {
491
 	cmd := &ModeCommand{
490
 	cmd := &ModeCommand{
492
-		nickname: args[0],
491
+		nickname: nickname,
493
 		changes:  make(ModeChanges, 0),
492
 		changes:  make(ModeChanges, 0),
494
 	}
493
 	}
495
 
494
 
496
-	for _, modeChange := range args[1:] {
495
+	for _, modeChange := range args {
497
 		if len(modeChange) == 0 {
496
 		if len(modeChange) == 0 {
498
 			continue
497
 			continue
499
 		}
498
 		}
559
 
558
 
560
 type ChannelModeCommand struct {
559
 type ChannelModeCommand struct {
561
 	BaseCommand
560
 	BaseCommand
562
-	channel string
561
+	channel Name
563
 	changes ChannelModeChanges
562
 	changes ChannelModeChanges
564
 }
563
 }
565
 
564
 
566
 // MODE <channel> *( ( "-" / "+" ) *<modes> *<modeparams> )
565
 // MODE <channel> *( ( "-" / "+" ) *<modes> *<modeparams> )
567
-func NewChannelModeCommand(args []string) (editableCommand, error) {
566
+func NewChannelModeCommand(channel Name, args []string) (editableCommand, error) {
568
 	cmd := &ChannelModeCommand{
567
 	cmd := &ChannelModeCommand{
569
-		channel: args[0],
568
+		channel: channel,
570
 		changes: make(ChannelModeChanges, 0),
569
 		changes: make(ChannelModeChanges, 0),
571
 	}
570
 	}
572
-	args = args[1:]
573
 
571
 
574
 	for len(args) > 0 {
572
 	for len(args) > 0 {
575
 		if len(args[0]) == 0 {
573
 		if len(args[0]) == 0 {
616
 		return nil, NotEnoughArgsError
614
 		return nil, NotEnoughArgsError
617
 	}
615
 	}
618
 
616
 
619
-	if IsChannel(args[0]) {
620
-		return NewChannelModeCommand(args)
617
+	name := NewName(args[0])
618
+	if name.IsChannel() {
619
+		return NewChannelModeCommand(name, args[1:])
621
 	} else {
620
 	} else {
622
-		return NewUserModeCommand(args)
621
+		return NewUserModeCommand(name, args[1:])
623
 	}
622
 	}
624
 }
623
 }
625
 
624
 
626
 type WhoisCommand struct {
625
 type WhoisCommand struct {
627
 	BaseCommand
626
 	BaseCommand
628
-	target string
629
-	masks  []string
627
+	target Name
628
+	masks  []Name
630
 }
629
 }
631
 
630
 
632
 // WHOIS [ <target> ] <mask> *( "," <mask> )
631
 // WHOIS [ <target> ] <mask> *( "," <mask> )
646
 	}
645
 	}
647
 
646
 
648
 	return &WhoisCommand{
647
 	return &WhoisCommand{
649
-		target: target,
650
-		masks:  strings.Split(masks, ","),
648
+		target: NewName(target),
649
+		masks:  NewNames(strings.Split(masks, ",")),
651
 	}, nil
650
 	}, nil
652
 }
651
 }
653
 
652
 
657
 
656
 
658
 type WhoCommand struct {
657
 type WhoCommand struct {
659
 	BaseCommand
658
 	BaseCommand
660
-	mask         string
659
+	mask         Name
661
 	operatorOnly bool
660
 	operatorOnly bool
662
 }
661
 }
663
 
662
 
666
 	cmd := &WhoCommand{}
665
 	cmd := &WhoCommand{}
667
 
666
 
668
 	if len(args) > 0 {
667
 	if len(args) > 0 {
669
-		cmd.mask = args[0]
668
+		cmd.mask = NewName(args[0])
670
 	}
669
 	}
671
 
670
 
672
 	if (len(args) > 1) && (args[1] == "o") {
671
 	if (len(args) > 1) && (args[1] == "o") {
682
 
681
 
683
 type OperCommand struct {
682
 type OperCommand struct {
684
 	PassCommand
683
 	PassCommand
685
-	name string
684
+	name Name
686
 }
685
 }
687
 
686
 
688
 func (msg *OperCommand) String() string {
687
 func (msg *OperCommand) String() string {
700
 	}
699
 	}
701
 
700
 
702
 	cmd := &OperCommand{
701
 	cmd := &OperCommand{
703
-		name: args[0],
702
+		name: NewName(args[0]),
704
 	}
703
 	}
705
 	cmd.password = []byte(args[1])
704
 	cmd.password = []byte(args[1])
706
 	return cmd, nil
705
 	return cmd, nil
739
 // HAPROXY support
738
 // HAPROXY support
740
 type ProxyCommand struct {
739
 type ProxyCommand struct {
741
 	BaseCommand
740
 	BaseCommand
742
-	net        string
743
-	sourceIP   string
744
-	destIP     string
745
-	sourcePort string
746
-	destPort   string
747
-	hostname   string // looked up in socket thread
741
+	net        Name
742
+	sourceIP   Name
743
+	destIP     Name
744
+	sourcePort Name
745
+	destPort   Name
746
+	hostname   Name // looked up in socket thread
748
 }
747
 }
749
 
748
 
750
 func (msg *ProxyCommand) String() string {
749
 func (msg *ProxyCommand) String() string {
756
 		return nil, NotEnoughArgsError
755
 		return nil, NotEnoughArgsError
757
 	}
756
 	}
758
 	return &ProxyCommand{
757
 	return &ProxyCommand{
759
-		net:        args[0],
760
-		sourceIP:   args[1],
761
-		destIP:     args[2],
762
-		sourcePort: args[3],
763
-		destPort:   args[4],
764
-		hostname:   LookupHostname(args[1]),
758
+		net:        NewName(args[0]),
759
+		sourceIP:   NewName(args[1]),
760
+		destIP:     NewName(args[2]),
761
+		sourcePort: NewName(args[3]),
762
+		destPort:   NewName(args[4]),
763
+		hostname:   LookupHostname(NewName(args[1])),
765
 	}, nil
764
 	}, nil
766
 }
765
 }
767
 
766
 
768
 type AwayCommand struct {
767
 type AwayCommand struct {
769
 	BaseCommand
768
 	BaseCommand
770
-	text string
769
+	text Text
771
 	away bool
770
 	away bool
772
 }
771
 }
773
 
772
 
779
 	cmd := &AwayCommand{}
778
 	cmd := &AwayCommand{}
780
 
779
 
781
 	if len(args) > 0 {
780
 	if len(args) > 0 {
782
-		cmd.text = args[0]
781
+		cmd.text = NewText(args[0])
783
 		cmd.away = true
782
 		cmd.away = true
784
 	}
783
 	}
785
 
784
 
788
 
787
 
789
 type IsOnCommand struct {
788
 type IsOnCommand struct {
790
 	BaseCommand
789
 	BaseCommand
791
-	nicks []string
790
+	nicks []Name
792
 }
791
 }
793
 
792
 
794
 func (msg *IsOnCommand) String() string {
793
 func (msg *IsOnCommand) String() string {
801
 	}
800
 	}
802
 
801
 
803
 	return &IsOnCommand{
802
 	return &IsOnCommand{
804
-		nicks: args,
803
+		nicks: NewNames(args),
805
 	}, nil
804
 	}, nil
806
 }
805
 }
807
 
806
 
808
 type MOTDCommand struct {
807
 type MOTDCommand struct {
809
 	BaseCommand
808
 	BaseCommand
810
-	target string
809
+	target Name
811
 }
810
 }
812
 
811
 
813
 func NewMOTDCommand(args []string) (editableCommand, error) {
812
 func NewMOTDCommand(args []string) (editableCommand, error) {
814
 	cmd := &MOTDCommand{}
813
 	cmd := &MOTDCommand{}
815
 	if len(args) > 0 {
814
 	if len(args) > 0 {
816
-		cmd.target = args[0]
815
+		cmd.target = NewName(args[0])
817
 	}
816
 	}
818
 	return cmd, nil
817
 	return cmd, nil
819
 }
818
 }
820
 
819
 
821
 type NoticeCommand struct {
820
 type NoticeCommand struct {
822
 	BaseCommand
821
 	BaseCommand
823
-	target  string
824
-	message string
822
+	target  Name
823
+	message Text
825
 }
824
 }
826
 
825
 
827
 func (cmd *NoticeCommand) String() string {
826
 func (cmd *NoticeCommand) String() string {
833
 		return nil, NotEnoughArgsError
832
 		return nil, NotEnoughArgsError
834
 	}
833
 	}
835
 	return &NoticeCommand{
834
 	return &NoticeCommand{
836
-		target:  args[0],
837
-		message: args[1],
835
+		target:  NewName(args[0]),
836
+		message: NewText(args[1]),
838
 	}, nil
837
 	}, nil
839
 }
838
 }
840
 
839
 
841
 type KickCommand struct {
840
 type KickCommand struct {
842
 	BaseCommand
841
 	BaseCommand
843
-	kicks   map[string]string
844
-	comment string
842
+	kicks   map[Name]Name
843
+	comment Text
845
 }
844
 }
846
 
845
 
847
-func (msg *KickCommand) Comment() string {
846
+func (msg *KickCommand) Comment() Text {
848
 	if msg.comment == "" {
847
 	if msg.comment == "" {
849
-		return msg.Client().Nick()
848
+		return msg.Client().Nick().Text()
850
 	}
849
 	}
851
 	return msg.comment
850
 	return msg.comment
852
 }
851
 }
855
 	if len(args) < 2 {
854
 	if len(args) < 2 {
856
 		return nil, NotEnoughArgsError
855
 		return nil, NotEnoughArgsError
857
 	}
856
 	}
858
-	channels := strings.Split(args[0], ",")
859
-	users := strings.Split(args[1], ",")
857
+	channels := NewNames(strings.Split(args[0], ","))
858
+	users := NewNames(strings.Split(args[1], ","))
860
 	if (len(channels) != len(users)) && (len(users) != 1) {
859
 	if (len(channels) != len(users)) && (len(users) != 1) {
861
 		return nil, NotEnoughArgsError
860
 		return nil, NotEnoughArgsError
862
 	}
861
 	}
863
 	cmd := &KickCommand{
862
 	cmd := &KickCommand{
864
-		kicks: make(map[string]string),
863
+		kicks: make(map[Name]Name),
865
 	}
864
 	}
866
 	for index, channel := range channels {
865
 	for index, channel := range channels {
867
 		if len(users) == 1 {
866
 		if len(users) == 1 {
871
 		}
870
 		}
872
 	}
871
 	}
873
 	if len(args) > 2 {
872
 	if len(args) > 2 {
874
-		cmd.comment = args[2]
873
+		cmd.comment = NewText(args[2])
875
 	}
874
 	}
876
 	return cmd, nil
875
 	return cmd, nil
877
 }
876
 }
878
 
877
 
879
 type ListCommand struct {
878
 type ListCommand struct {
880
 	BaseCommand
879
 	BaseCommand
881
-	channels []string
882
-	target   string
880
+	channels []Name
881
+	target   Name
883
 }
882
 }
884
 
883
 
885
 func NewListCommand(args []string) (editableCommand, error) {
884
 func NewListCommand(args []string) (editableCommand, error) {
886
 	cmd := &ListCommand{}
885
 	cmd := &ListCommand{}
887
 	if len(args) > 0 {
886
 	if len(args) > 0 {
888
-		cmd.channels = strings.Split(args[0], ",")
887
+		cmd.channels = NewNames(strings.Split(args[0], ","))
889
 	}
888
 	}
890
 	if len(args) > 1 {
889
 	if len(args) > 1 {
891
-		cmd.target = args[1]
890
+		cmd.target = NewName(args[1])
892
 	}
891
 	}
893
 	return cmd, nil
892
 	return cmd, nil
894
 }
893
 }
895
 
894
 
896
 type NamesCommand struct {
895
 type NamesCommand struct {
897
 	BaseCommand
896
 	BaseCommand
898
-	channels []string
899
-	target   string
897
+	channels []Name
898
+	target   Name
900
 }
899
 }
901
 
900
 
902
 func NewNamesCommand(args []string) (editableCommand, error) {
901
 func NewNamesCommand(args []string) (editableCommand, error) {
903
 	cmd := &NamesCommand{}
902
 	cmd := &NamesCommand{}
904
 	if len(args) > 0 {
903
 	if len(args) > 0 {
905
-		cmd.channels = strings.Split(args[0], ",")
904
+		cmd.channels = NewNames(strings.Split(args[0], ","))
906
 	}
905
 	}
907
 	if len(args) > 1 {
906
 	if len(args) > 1 {
908
-		cmd.target = args[1]
907
+		cmd.target = NewName(args[1])
909
 	}
908
 	}
910
 	return cmd, nil
909
 	return cmd, nil
911
 }
910
 }
912
 
911
 
913
 type DebugCommand struct {
912
 type DebugCommand struct {
914
 	BaseCommand
913
 	BaseCommand
915
-	subCommand string
914
+	subCommand Name
916
 }
915
 }
917
 
916
 
918
 func NewDebugCommand(args []string) (editableCommand, error) {
917
 func NewDebugCommand(args []string) (editableCommand, error) {
921
 	}
920
 	}
922
 
921
 
923
 	return &DebugCommand{
922
 	return &DebugCommand{
924
-		subCommand: strings.ToUpper(args[0]),
923
+		subCommand: NewName(strings.ToUpper(args[0])),
925
 	}, nil
924
 	}, nil
926
 }
925
 }
927
 
926
 
928
 type VersionCommand struct {
927
 type VersionCommand struct {
929
 	BaseCommand
928
 	BaseCommand
930
-	target string
929
+	target Name
931
 }
930
 }
932
 
931
 
933
 func NewVersionCommand(args []string) (editableCommand, error) {
932
 func NewVersionCommand(args []string) (editableCommand, error) {
934
 	cmd := &VersionCommand{}
933
 	cmd := &VersionCommand{}
935
 	if len(args) > 0 {
934
 	if len(args) > 0 {
936
-		cmd.target = args[0]
935
+		cmd.target = NewName(args[0])
937
 	}
936
 	}
938
 	return cmd, nil
937
 	return cmd, nil
939
 }
938
 }
940
 
939
 
941
 type InviteCommand struct {
940
 type InviteCommand struct {
942
 	BaseCommand
941
 	BaseCommand
943
-	nickname string
944
-	channel  string
942
+	nickname Name
943
+	channel  Name
945
 }
944
 }
946
 
945
 
947
 func NewInviteCommand(args []string) (editableCommand, error) {
946
 func NewInviteCommand(args []string) (editableCommand, error) {
950
 	}
949
 	}
951
 
950
 
952
 	return &InviteCommand{
951
 	return &InviteCommand{
953
-		nickname: args[0],
954
-		channel:  args[1],
952
+		nickname: NewName(args[0]),
953
+		channel:  NewName(args[1]),
955
 	}, nil
954
 	}, nil
956
 }
955
 }
957
 
956
 
958
 type TimeCommand struct {
957
 type TimeCommand struct {
959
 	BaseCommand
958
 	BaseCommand
960
-	target string
959
+	target Name
961
 }
960
 }
962
 
961
 
963
 func NewTimeCommand(args []string) (editableCommand, error) {
962
 func NewTimeCommand(args []string) (editableCommand, error) {
964
 	cmd := &TimeCommand{}
963
 	cmd := &TimeCommand{}
965
 	if len(args) > 0 {
964
 	if len(args) > 0 {
966
-		cmd.target = args[0]
965
+		cmd.target = NewName(args[0])
967
 	}
966
 	}
968
 	return cmd, nil
967
 	return cmd, nil
969
 }
968
 }
970
 
969
 
971
 type KillCommand struct {
970
 type KillCommand struct {
972
 	BaseCommand
971
 	BaseCommand
973
-	nickname string
974
-	comment  string
972
+	nickname Name
973
+	comment  Text
975
 }
974
 }
976
 
975
 
977
 func NewKillCommand(args []string) (editableCommand, error) {
976
 func NewKillCommand(args []string) (editableCommand, error) {
979
 		return nil, NotEnoughArgsError
978
 		return nil, NotEnoughArgsError
980
 	}
979
 	}
981
 	return &KillCommand{
980
 	return &KillCommand{
982
-		nickname: args[0],
983
-		comment:  args[1],
981
+		nickname: NewName(args[0]),
982
+		comment:  NewText(args[1]),
984
 	}, nil
983
 	}, nil
985
 }
984
 }
986
 
985
 
987
 type WhoWasCommand struct {
986
 type WhoWasCommand struct {
988
 	BaseCommand
987
 	BaseCommand
989
-	nicknames []string
988
+	nicknames []Name
990
 	count     int64
989
 	count     int64
991
-	target    string
990
+	target    Name
992
 }
991
 }
993
 
992
 
994
 func NewWhoWasCommand(args []string) (editableCommand, error) {
993
 func NewWhoWasCommand(args []string) (editableCommand, error) {
996
 		return nil, NotEnoughArgsError
995
 		return nil, NotEnoughArgsError
997
 	}
996
 	}
998
 	cmd := &WhoWasCommand{
997
 	cmd := &WhoWasCommand{
999
-		nicknames: strings.Split(args[0], ","),
998
+		nicknames: NewNames(strings.Split(args[0], ",")),
1000
 	}
999
 	}
1001
 	if len(args) > 1 {
1000
 	if len(args) > 1 {
1002
 		cmd.count, _ = strconv.ParseInt(args[1], 10, 64)
1001
 		cmd.count, _ = strconv.ParseInt(args[1], 10, 64)
1003
 	}
1002
 	}
1004
 	if len(args) > 2 {
1003
 	if len(args) > 2 {
1005
-		cmd.target = args[2]
1004
+		cmd.target = NewName(args[2])
1006
 	}
1005
 	}
1007
 	return cmd, nil
1006
 	return cmd, nil
1008
 }
1007
 }

+ 3
- 3
irc/config.go Целия файл

37
 	}
37
 	}
38
 }
38
 }
39
 
39
 
40
-func (conf *Config) Operators() map[string][]byte {
41
-	operators := make(map[string][]byte)
40
+func (conf *Config) Operators() map[Name][]byte {
41
+	operators := make(map[Name][]byte)
42
 	for name, opConf := range conf.Operator {
42
 	for name, opConf := range conf.Operator {
43
-		operators[name] = opConf.PasswordBytes()
43
+		operators[NewName(name)] = opConf.PasswordBytes()
44
 	}
44
 	}
45
 	return operators
45
 	return operators
46
 }
46
 }

+ 0
- 6
irc/constants.go Целия файл

2
 
2
 
3
 import (
3
 import (
4
 	"errors"
4
 	"errors"
5
-	"regexp"
6
 	"time"
5
 	"time"
7
 )
6
 )
8
 
7
 
15
 
14
 
16
 	// errors
15
 	// errors
17
 	ErrAlreadyDestroyed = errors.New("already destroyed")
16
 	ErrAlreadyDestroyed = errors.New("already destroyed")
18
-
19
-	// regexps
20
-	ChannelNameExpr = regexp.MustCompile(`^[&!#+][\pL\pN]{1,63}$`)
21
-	NicknameExpr    = regexp.MustCompile(
22
-		"^[\\pL\\pN\\pP\\pS]{1,32}$")
23
 )
17
 )
24
 
18
 
25
 const (
19
 const (

+ 8
- 8
irc/net.go Целия файл

5
 	"strings"
5
 	"strings"
6
 )
6
 )
7
 
7
 
8
-func IPString(addr net.Addr) string {
8
+func IPString(addr net.Addr) Name {
9
 	addrStr := addr.String()
9
 	addrStr := addr.String()
10
 	ipaddr, _, err := net.SplitHostPort(addrStr)
10
 	ipaddr, _, err := net.SplitHostPort(addrStr)
11
 	if err != nil {
11
 	if err != nil {
12
-		return addrStr
12
+		return Name(addrStr)
13
 	}
13
 	}
14
-	return ipaddr
14
+	return Name(ipaddr)
15
 }
15
 }
16
 
16
 
17
-func AddrLookupHostname(addr net.Addr) string {
17
+func AddrLookupHostname(addr net.Addr) Name {
18
 	return LookupHostname(IPString(addr))
18
 	return LookupHostname(IPString(addr))
19
 }
19
 }
20
 
20
 
21
-func LookupHostname(addr string) string {
22
-	names, err := net.LookupAddr(addr)
21
+func LookupHostname(addr Name) Name {
22
+	names, err := net.LookupAddr(addr.String())
23
 	if err != nil {
23
 	if err != nil {
24
-		return addr
24
+		return Name(addr)
25
 	}
25
 	}
26
 
26
 
27
 	hostname := strings.TrimSuffix(names[0], ".")
27
 	hostname := strings.TrimSuffix(names[0], ".")
28
-	return hostname
28
+	return Name(hostname)
29
 }
29
 }

+ 27
- 27
irc/reply.go Целия файл

79
 // messaging replies
79
 // messaging replies
80
 //
80
 //
81
 
81
 
82
-func RplPrivMsg(source Identifier, target Identifier, message string) string {
82
+func RplPrivMsg(source Identifier, target Identifier, message Text) string {
83
 	return NewStringReply(source, PRIVMSG, "%s :%s", target.Nick(), message)
83
 	return NewStringReply(source, PRIVMSG, "%s :%s", target.Nick(), message)
84
 }
84
 }
85
 
85
 
86
-func RplNotice(source Identifier, target Identifier, message string) string {
86
+func RplNotice(source Identifier, target Identifier, message Text) string {
87
 	return NewStringReply(source, NOTICE, "%s :%s", target.Nick(), message)
87
 	return NewStringReply(source, NOTICE, "%s :%s", target.Nick(), message)
88
 }
88
 }
89
 
89
 
90
-func RplNick(source Identifier, newNick string) string {
91
-	return NewStringReply(source, NICK, newNick)
90
+func RplNick(source Identifier, newNick Name) string {
91
+	return NewStringReply(source, NICK, newNick.String())
92
 }
92
 }
93
 
93
 
94
 func RplJoin(client *Client, channel *Channel) string {
94
 func RplJoin(client *Client, channel *Channel) string {
95
-	return NewStringReply(client, JOIN, channel.name)
95
+	return NewStringReply(client, JOIN, channel.name.String())
96
 }
96
 }
97
 
97
 
98
-func RplPart(client *Client, channel *Channel, message string) string {
98
+func RplPart(client *Client, channel *Channel, message Text) string {
99
 	return NewStringReply(client, PART, "%s :%s", channel, message)
99
 	return NewStringReply(client, PART, "%s :%s", channel, message)
100
 }
100
 }
101
 
101
 
117
 }
117
 }
118
 
118
 
119
 func RplPong(client *Client) string {
119
 func RplPong(client *Client) string {
120
-	return NewStringReply(nil, PONG, client.Nick())
120
+	return NewStringReply(nil, PONG, client.Nick().String())
121
 }
121
 }
122
 
122
 
123
-func RplQuit(client *Client, message string) string {
123
+func RplQuit(client *Client, message Text) string {
124
 	return NewStringReply(client, QUIT, ":%s", message)
124
 	return NewStringReply(client, QUIT, ":%s", message)
125
 }
125
 }
126
 
126
 
128
 	return NewStringReply(nil, ERROR, ":%s", message)
128
 	return NewStringReply(nil, ERROR, ":%s", message)
129
 }
129
 }
130
 
130
 
131
-func RplInviteMsg(inviter *Client, invitee *Client, channel string) string {
131
+func RplInviteMsg(inviter *Client, invitee *Client, channel Name) string {
132
 	return NewStringReply(inviter, INVITE, "%s :%s", invitee.Nick(), channel)
132
 	return NewStringReply(inviter, INVITE, "%s :%s", invitee.Nick(), channel)
133
 }
133
 }
134
 
134
 
135
-func RplKick(channel *Channel, client *Client, target *Client, comment string) string {
135
+func RplKick(channel *Channel, client *Client, target *Client, comment Text) string {
136
 	return NewStringReply(client, KICK, "%s %s :%s",
136
 	return NewStringReply(client, KICK, "%s %s :%s",
137
 		channel, target.Nick(), comment)
137
 		channel, target.Nick(), comment)
138
 }
138
 }
139
 
139
 
140
-func RplKill(client *Client, target *Client, comment string) string {
140
+func RplKill(client *Client, target *Client, comment Text) string {
141
 	return NewStringReply(client, KICK,
141
 	return NewStringReply(client, KICK,
142
 		"%s :%s", target.Nick(), comment)
142
 		"%s :%s", target.Nick(), comment)
143
 }
143
 }
184
 
184
 
185
 // <nick> <channel>
185
 // <nick> <channel>
186
 // NB: correction in errata
186
 // NB: correction in errata
187
-func (target *Client) RplInvitingMsg(invitee *Client, channel string) {
187
+func (target *Client) RplInvitingMsg(invitee *Client, channel Name) {
188
 	target.NumericReply(RPL_INVITING,
188
 	target.NumericReply(RPL_INVITING,
189
 		"%s %s", invitee.Nick(), channel)
189
 		"%s %s", invitee.Nick(), channel)
190
 }
190
 }
253
 	}
253
 	}
254
 
254
 
255
 	if channel != nil {
255
 	if channel != nil {
256
-		channelName = channel.name
256
+		channelName = channel.name.String()
257
 		if target.capabilities[MultiPrefix] {
257
 		if target.capabilities[MultiPrefix] {
258
 			if channel.members[client][ChannelOperator] {
258
 			if channel.members[client][ChannelOperator] {
259
 				flags += "@"
259
 				flags += "@"
275
 }
275
 }
276
 
276
 
277
 // <name> :End of WHO list
277
 // <name> :End of WHO list
278
-func (target *Client) RplEndOfWho(name string) {
278
+func (target *Client) RplEndOfWho(name Name) {
279
 	target.NumericReply(RPL_ENDOFWHO,
279
 	target.NumericReply(RPL_ENDOFWHO,
280
 		"%s :End of WHO list", name)
280
 		"%s :End of WHO list", name)
281
 }
281
 }
282
 
282
 
283
-func (target *Client) RplMaskList(mode ChannelMode, channel *Channel, mask string) {
283
+func (target *Client) RplMaskList(mode ChannelMode, channel *Channel, mask Name) {
284
 	switch mode {
284
 	switch mode {
285
 	case BanMask:
285
 	case BanMask:
286
 		target.RplBanList(channel, mask)
286
 		target.RplBanList(channel, mask)
306
 	}
306
 	}
307
 }
307
 }
308
 
308
 
309
-func (target *Client) RplBanList(channel *Channel, mask string) {
309
+func (target *Client) RplBanList(channel *Channel, mask Name) {
310
 	target.NumericReply(RPL_BANLIST,
310
 	target.NumericReply(RPL_BANLIST,
311
 		"%s %s", channel, mask)
311
 		"%s %s", channel, mask)
312
 }
312
 }
316
 		"%s :End of channel ban list", channel)
316
 		"%s :End of channel ban list", channel)
317
 }
317
 }
318
 
318
 
319
-func (target *Client) RplExceptList(channel *Channel, mask string) {
319
+func (target *Client) RplExceptList(channel *Channel, mask Name) {
320
 	target.NumericReply(RPL_EXCEPTLIST,
320
 	target.NumericReply(RPL_EXCEPTLIST,
321
 		"%s %s", channel, mask)
321
 		"%s %s", channel, mask)
322
 }
322
 }
326
 		"%s :End of channel exception list", channel)
326
 		"%s :End of channel exception list", channel)
327
 }
327
 }
328
 
328
 
329
-func (target *Client) RplInviteList(channel *Channel, mask string) {
329
+func (target *Client) RplInviteList(channel *Channel, mask Name) {
330
 	target.NumericReply(RPL_INVITELIST,
330
 	target.NumericReply(RPL_INVITELIST,
331
 		"%s %s", channel, mask)
331
 		"%s %s", channel, mask)
332
 }
332
 }
396
 		"%s %s", SEM_VER, target.server.name)
396
 		"%s %s", SEM_VER, target.server.name)
397
 }
397
 }
398
 
398
 
399
-func (target *Client) RplInviting(invitee *Client, channel string) {
399
+func (target *Client) RplInviting(invitee *Client, channel Name) {
400
 	target.NumericReply(RPL_INVITING,
400
 	target.NumericReply(RPL_INVITING,
401
 		"%s %s", invitee.Nick(), channel)
401
 		"%s %s", invitee.Nick(), channel)
402
 }
402
 }
412
 		whoWas.nickname, whoWas.username, whoWas.hostname, whoWas.realname)
412
 		whoWas.nickname, whoWas.username, whoWas.hostname, whoWas.realname)
413
 }
413
 }
414
 
414
 
415
-func (target *Client) RplEndOfWhoWas(nickname string) {
415
+func (target *Client) RplEndOfWhoWas(nickname Name) {
416
 	target.NumericReply(RPL_ENDOFWHOWAS,
416
 	target.NumericReply(RPL_ENDOFWHOWAS,
417
 		"%s :End of WHOWAS", nickname)
417
 		"%s :End of WHOWAS", nickname)
418
 }
418
 }
426
 		":You may not reregister")
426
 		":You may not reregister")
427
 }
427
 }
428
 
428
 
429
-func (target *Client) ErrNickNameInUse(nick string) {
429
+func (target *Client) ErrNickNameInUse(nick Name) {
430
 	target.NumericReply(ERR_NICKNAMEINUSE,
430
 	target.NumericReply(ERR_NICKNAMEINUSE,
431
 		"%s :Nickname is already in use", nick)
431
 		"%s :Nickname is already in use", nick)
432
 }
432
 }
441
 		":Cannot change mode for other users")
441
 		":Cannot change mode for other users")
442
 }
442
 }
443
 
443
 
444
-func (target *Client) ErrNeedMoreParams(command string) {
444
+func (target *Client) ErrNeedMoreParams(command StringCode) {
445
 	target.NumericReply(ERR_NEEDMOREPARAMS,
445
 	target.NumericReply(ERR_NEEDMOREPARAMS,
446
 		"%s :Not enough parameters", command)
446
 		"%s :Not enough parameters", command)
447
 }
447
 }
448
 
448
 
449
-func (target *Client) ErrNoSuchChannel(channel string) {
449
+func (target *Client) ErrNoSuchChannel(channel Name) {
450
 	target.NumericReply(ERR_NOSUCHCHANNEL,
450
 	target.NumericReply(ERR_NOSUCHCHANNEL,
451
 		"%s :No such channel", channel)
451
 		"%s :No such channel", channel)
452
 }
452
 }
471
 		"%s :Cannot join channel (+k)", channel.name)
471
 		"%s :Cannot join channel (+k)", channel.name)
472
 }
472
 }
473
 
473
 
474
-func (target *Client) ErrNoSuchNick(nick string) {
474
+func (target *Client) ErrNoSuchNick(nick Name) {
475
 	target.NumericReply(ERR_NOSUCHNICK,
475
 	target.NumericReply(ERR_NOSUCHNICK,
476
 		"%s :No such nick/channel", nick)
476
 		"%s :No such nick/channel", nick)
477
 }
477
 }
493
 	target.NumericReply(ERR_RESTRICTED, ":Your connection is restricted!")
493
 	target.NumericReply(ERR_RESTRICTED, ":Your connection is restricted!")
494
 }
494
 }
495
 
495
 
496
-func (target *Client) ErrNoSuchServer(server string) {
496
+func (target *Client) ErrNoSuchServer(server Name) {
497
 	target.NumericReply(ERR_NOSUCHSERVER, "%s :No such server", server)
497
 	target.NumericReply(ERR_NOSUCHSERVER, "%s :No such server", server)
498
 }
498
 }
499
 
499
 
521
 	target.NumericReply(ERR_NONICKNAMEGIVEN, ":No nickname given")
521
 	target.NumericReply(ERR_NONICKNAMEGIVEN, ":No nickname given")
522
 }
522
 }
523
 
523
 
524
-func (target *Client) ErrErroneusNickname(nick string) {
524
+func (target *Client) ErrErroneusNickname(nick Name) {
525
 	target.NumericReply(ERR_ERRONEUSNICKNAME,
525
 	target.NumericReply(ERR_ERRONEUSNICKNAME,
526
 		"%s :Erroneous nickname", nick)
526
 		"%s :Erroneous nickname", nick)
527
 }
527
 }
536
 		"%s :Cannot join channel (+l)", channel)
536
 		"%s :Cannot join channel (+l)", channel)
537
 }
537
 }
538
 
538
 
539
-func (target *Client) ErrWasNoSuchNick(nickname string) {
539
+func (target *Client) ErrWasNoSuchNick(nickname Name) {
540
 	target.NumericReply(ERR_WASNOSUCHNICK,
540
 	target.NumericReply(ERR_WASNOSUCHNICK,
541
 		"%s :There was no such nickname", nickname)
541
 		"%s :There was no such nickname", nickname)
542
 }
542
 }

+ 23
- 21
irc/server.go Целия файл

24
 	db        *sql.DB
24
 	db        *sql.DB
25
 	idle      chan *Client
25
 	idle      chan *Client
26
 	motdFile  string
26
 	motdFile  string
27
-	name      string
27
+	name      Name
28
 	newConns  chan net.Conn
28
 	newConns  chan net.Conn
29
-	operators map[string][]byte
29
+	operators map[Name][]byte
30
 	password  []byte
30
 	password  []byte
31
 	signals   chan os.Signal
31
 	signals   chan os.Signal
32
 	whoWas    *WhoWasList
32
 	whoWas    *WhoWasList
41
 		db:        OpenDB(config.Server.Database),
41
 		db:        OpenDB(config.Server.Database),
42
 		idle:      make(chan *Client, 16),
42
 		idle:      make(chan *Client, 16),
43
 		motdFile:  config.Server.MOTD,
43
 		motdFile:  config.Server.MOTD,
44
-		name:      config.Server.Name,
44
+		name:      NewName(config.Server.Name),
45
 		newConns:  make(chan net.Conn, 16),
45
 		newConns:  make(chan net.Conn, 16),
46
 		operators: config.Operators(),
46
 		operators: config.Operators(),
47
 		signals:   make(chan os.Signal, 1),
47
 		signals:   make(chan os.Signal, 1),
68
 	if list == "" {
68
 	if list == "" {
69
 		return
69
 		return
70
 	}
70
 	}
71
-	channel.lists[maskMode].AddAll(strings.Split(list, " "))
71
+	channel.lists[maskMode].AddAll(NewNames(strings.Split(list, " ")))
72
 }
72
 }
73
 
73
 
74
 func (server *Server) loadChannels() {
74
 func (server *Server) loadChannels() {
80
 		log.Fatal("error loading channels: ", err)
80
 		log.Fatal("error loading channels: ", err)
81
 	}
81
 	}
82
 	for rows.Next() {
82
 	for rows.Next() {
83
-		var name, flags, key, topic string
83
+		var name Name
84
+		var flags string
85
+		var key, topic Text
84
 		var userLimit uint64
86
 		var userLimit uint64
85
 		var banList, exceptList, inviteList string
87
 		var banList, exceptList, inviteList string
86
 		err = rows.Scan(&name, &flags, &key, &topic, &userLimit, &banList,
88
 		err = rows.Scan(&name, &flags, &key, &topic, &userLimit, &banList,
248
 	client.RplMOTDEnd()
250
 	client.RplMOTDEnd()
249
 }
251
 }
250
 
252
 
251
-func (s *Server) Id() string {
253
+func (s *Server) Id() Name {
252
 	return s.name
254
 	return s.name
253
 }
255
 }
254
 
256
 
255
 func (s *Server) String() string {
257
 func (s *Server) String() string {
256
-	return s.name
258
+	return s.name.String()
257
 }
259
 }
258
 
260
 
259
-func (s *Server) Nick() string {
261
+func (s *Server) Nick() Name {
260
 	return s.Id()
262
 	return s.Id()
261
 }
263
 }
262
 
264
 
339
 		return
341
 		return
340
 	}
342
 	}
341
 
343
 
342
-	if !IsNickname(m.nickname) {
344
+	if !m.nickname.IsNickname() {
343
 		client.ErrErroneusNickname(m.nickname)
345
 		client.ErrErroneusNickname(m.nickname)
344
 		return
346
 		return
345
 	}
347
 	}
416
 		return
418
 		return
417
 	}
419
 	}
418
 
420
 
419
-	if !IsNickname(msg.nickname) {
421
+	if !msg.nickname.IsNickname() {
420
 		client.ErrErroneusNickname(msg.nickname)
422
 		client.ErrErroneusNickname(msg.nickname)
421
 		return
423
 		return
422
 	}
424
 	}
447
 
449
 
448
 	if m.zero {
450
 	if m.zero {
449
 		for channel := range client.channels {
451
 		for channel := range client.channels {
450
-			channel.Part(client, client.Nick())
452
+			channel.Part(client, client.Nick().Text())
451
 		}
453
 		}
452
 		return
454
 		return
453
 	}
455
 	}
454
 
456
 
455
 	for name, key := range m.channels {
457
 	for name, key := range m.channels {
456
-		if !IsChannel(name) {
458
+		if !name.IsChannel() {
457
 			client.ErrNoSuchChannel(name)
459
 			client.ErrNoSuchChannel(name)
458
 			continue
460
 			continue
459
 		}
461
 		}
497
 
499
 
498
 func (msg *PrivMsgCommand) HandleServer(server *Server) {
500
 func (msg *PrivMsgCommand) HandleServer(server *Server) {
499
 	client := msg.Client()
501
 	client := msg.Client()
500
-	if IsChannel(msg.target) {
502
+	if msg.target.IsChannel() {
501
 		channel := server.channels.Get(msg.target)
503
 		channel := server.channels.Get(msg.target)
502
 		if channel == nil {
504
 		if channel == nil {
503
 			client.ErrNoSuchChannel(msg.target)
505
 			client.ErrNoSuchChannel(msg.target)
577
 	for channel := range client.channels {
579
 	for channel := range client.channels {
578
 		switch {
580
 		switch {
579
 		case channel.members[client][ChannelOperator]:
581
 		case channel.members[client][ChannelOperator]:
580
-			chstrs[index] = "@" + channel.name
582
+			chstrs[index] = "@" + channel.name.String()
581
 
583
 
582
 		case channel.members[client][Voice]:
584
 		case channel.members[client][Voice]:
583
-			chstrs[index] = "+" + channel.name
585
+			chstrs[index] = "+" + channel.name.String()
584
 
586
 
585
 		default:
587
 		default:
586
-			chstrs[index] = channel.name
588
+			chstrs[index] = channel.name.String()
587
 		}
589
 		}
588
 		index += 1
590
 		index += 1
589
 	}
591
 	}
635
 		for _, channel := range server.channels {
637
 		for _, channel := range server.channels {
636
 			whoChannel(client, channel, friends)
638
 			whoChannel(client, channel, friends)
637
 		}
639
 		}
638
-	} else if IsChannel(mask) {
640
+	} else if mask.IsChannel() {
639
 		// TODO implement wildcard matching
641
 		// TODO implement wildcard matching
640
 		channel := server.channels.Get(mask)
642
 		channel := server.channels.Get(mask)
641
 		if channel != nil {
643
 		if channel != nil {
685
 	ison := make([]string, 0)
687
 	ison := make([]string, 0)
686
 	for _, nick := range msg.nicks {
688
 	for _, nick := range msg.nicks {
687
 		if iclient := server.clients.Get(nick); iclient != nil {
689
 		if iclient := server.clients.Get(nick); iclient != nil {
688
-			ison = append(ison, iclient.Nick())
690
+			ison = append(ison, iclient.Nick().String())
689
 		}
691
 		}
690
 	}
692
 	}
691
 
693
 
698
 
700
 
699
 func (msg *NoticeCommand) HandleServer(server *Server) {
701
 func (msg *NoticeCommand) HandleServer(server *Server) {
700
 	client := msg.Client()
702
 	client := msg.Client()
701
-	if IsChannel(msg.target) {
703
+	if msg.target.IsChannel() {
702
 		channel := server.channels.Get(msg.target)
704
 		channel := server.channels.Get(msg.target)
703
 		if channel == nil {
705
 		if channel == nil {
704
 			client.ErrNoSuchChannel(msg.target)
706
 			client.ErrNoSuchChannel(msg.target)
785
 }
787
 }
786
 
788
 
787
 func (server *Server) Reply(target *Client, format string, args ...interface{}) {
789
 func (server *Server) Reply(target *Client, format string, args ...interface{}) {
788
-	target.Reply(RplPrivMsg(server, target, fmt.Sprintf(format, args...)))
790
+	target.Reply(RplPrivMsg(server, target, NewText(fmt.Sprintf(format, args...))))
789
 }
791
 }
790
 
792
 
791
 func (msg *DebugCommand) HandleServer(server *Server) {
793
 func (msg *DebugCommand) HandleServer(server *Server) {
880
 	}
882
 	}
881
 
883
 
882
 	quitMsg := fmt.Sprintf("KILLed by %s: %s", client.Nick(), msg.comment)
884
 	quitMsg := fmt.Sprintf("KILLed by %s: %s", client.Nick(), msg.comment)
883
-	target.Quit(quitMsg)
885
+	target.Quit(NewText(quitMsg))
884
 }
886
 }
885
 
887
 
886
 func (msg *WhoWasCommand) HandleServer(server *Server) {
888
 func (msg *WhoWasCommand) HandleServer(server *Server) {

+ 66
- 0
irc/strings.go Целия файл

1
+package irc
2
+
3
+import (
4
+	"code.google.com/p/go.text/unicode/norm"
5
+	"regexp"
6
+	"strings"
7
+)
8
+
9
+var (
10
+	// regexps
11
+	ChannelNameExpr = regexp.MustCompile(`^[&!#+][\pL\pN]{1,63}$`)
12
+	NicknameExpr    = regexp.MustCompile("^[\\pL\\pN\\pP\\pS]{1,32}$")
13
+)
14
+
15
+// Names are normalized and canonicalized to remove formatting marks
16
+// and simplify usage. They are things like hostnames and usermasks.
17
+type Name string
18
+
19
+func NewName(str string) Name {
20
+	return Name(norm.NFKC.String(str))
21
+}
22
+
23
+func NewNames(strs []string) []Name {
24
+	names := make([]Name, len(strs))
25
+	for index, str := range strs {
26
+		names[index] = NewName(str)
27
+	}
28
+	return names
29
+}
30
+
31
+// tests
32
+
33
+func (name Name) IsChannel() bool {
34
+	return ChannelNameExpr.MatchString(name.String())
35
+}
36
+
37
+func (name Name) IsNickname() bool {
38
+	return NicknameExpr.MatchString(name.String())
39
+}
40
+
41
+// conversions
42
+
43
+func (name Name) String() string {
44
+	return string(name)
45
+}
46
+
47
+func (name Name) ToLower() Name {
48
+	return Name(strings.ToLower(name.String()))
49
+}
50
+
51
+// It's safe to coerce a Name to Text. Name is a strict subset of Text.
52
+func (name Name) Text() Text {
53
+	return Text(name)
54
+}
55
+
56
+// Text is PRIVMSG, NOTICE, or TOPIC data. It's canonicalized UTF8
57
+// data to simplify but keeps all formatting.
58
+type Text string
59
+
60
+func NewText(str string) Text {
61
+	return Text(norm.NFC.String(str))
62
+}
63
+
64
+func (text Text) String() string {
65
+	return string(text)
66
+}

+ 10
- 10
irc/types.go Целия файл

67
 	String() string
67
 	String() string
68
 }
68
 }
69
 
69
 
70
-type StringCode string
70
+type StringCode Name
71
 
71
 
72
 func (code StringCode) String() string {
72
 func (code StringCode) String() string {
73
 	return string(code)
73
 	return string(code)
86
 	return string(mode)
86
 	return string(mode)
87
 }
87
 }
88
 
88
 
89
-type ChannelNameMap map[string]*Channel
89
+type ChannelNameMap map[Name]*Channel
90
 
90
 
91
-func (channels ChannelNameMap) Get(name string) *Channel {
92
-	return channels[strings.ToLower(name)]
91
+func (channels ChannelNameMap) Get(name Name) *Channel {
92
+	return channels[name.ToLower()]
93
 }
93
 }
94
 
94
 
95
 func (channels ChannelNameMap) Add(channel *Channel) error {
95
 func (channels ChannelNameMap) Add(channel *Channel) error {
96
-	if channels[channel.name] != nil {
96
+	if channels[channel.name.ToLower()] != nil {
97
 		return fmt.Errorf("%s: already set", channel.name)
97
 		return fmt.Errorf("%s: already set", channel.name)
98
 	}
98
 	}
99
-	channels[channel.name] = channel
99
+	channels[channel.name.ToLower()] = channel
100
 	return nil
100
 	return nil
101
 }
101
 }
102
 
102
 
103
 func (channels ChannelNameMap) Remove(channel *Channel) error {
103
 func (channels ChannelNameMap) Remove(channel *Channel) error {
104
-	if channel != channels[channel.name] {
104
+	if channel != channels[channel.name.ToLower()] {
105
 		return fmt.Errorf("%s: mismatch", channel.name)
105
 		return fmt.Errorf("%s: mismatch", channel.name)
106
 	}
106
 	}
107
-	delete(channels, channel.name)
107
+	delete(channels, channel.name.ToLower())
108
 	return nil
108
 	return nil
109
 }
109
 }
110
 
110
 
182
 //
182
 //
183
 
183
 
184
 type Identifier interface {
184
 type Identifier interface {
185
-	Id() string
186
-	Nick() string
185
+	Id() Name
186
+	Nick() Name
187
 }
187
 }
188
 
188
 
189
 type Replier interface {
189
 type Replier interface {

+ 5
- 5
irc/whowas.go Целия файл

7
 }
7
 }
8
 
8
 
9
 type WhoWas struct {
9
 type WhoWas struct {
10
-	nickname string
11
-	username string
12
-	hostname string
13
-	realname string
10
+	nickname Name
11
+	username Name
12
+	hostname Name
13
+	realname Text
14
 }
14
 }
15
 
15
 
16
 func NewWhoWasList(size uint) *WhoWasList {
16
 func NewWhoWasList(size uint) *WhoWasList {
32
 	}
32
 	}
33
 }
33
 }
34
 
34
 
35
-func (list *WhoWasList) Find(nickname string, limit int64) []*WhoWas {
35
+func (list *WhoWasList) Find(nickname Name, limit int64) []*WhoWas {
36
 	results := make([]*WhoWas, 0)
36
 	results := make([]*WhoWas, 0)
37
 	for whoWas := range list.Each() {
37
 	for whoWas := range list.Each() {
38
 		if nickname != whoWas.nickname {
38
 		if nickname != whoWas.nickname {

Loading…
Отказ
Запис