Bläddra i källkod

mark unicode normalization with type

tags/v0.1.0
Jeremy Latt 10 år sedan
förälder
incheckning
96a108f8da
12 ändrade filer med 324 tillägg och 269 borttagningar
  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 Visa fil

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

+ 15
- 19
irc/client.go Visa fil

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

+ 24
- 24
irc/client_lookup_set.go Visa fil

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

+ 117
- 118
irc/commands.go Visa fil

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

+ 3
- 3
irc/config.go Visa fil

@@ -37,10 +37,10 @@ type Config struct {
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 42
 	for name, opConf := range conf.Operator {
43
-		operators[name] = opConf.PasswordBytes()
43
+		operators[NewName(name)] = opConf.PasswordBytes()
44 44
 	}
45 45
 	return operators
46 46
 }

+ 0
- 6
irc/constants.go Visa fil

@@ -2,7 +2,6 @@ package irc
2 2
 
3 3
 import (
4 4
 	"errors"
5
-	"regexp"
6 5
 	"time"
7 6
 )
8 7
 
@@ -15,11 +14,6 @@ var (
15 14
 
16 15
 	// errors
17 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 19
 const (

+ 8
- 8
irc/net.go Visa fil

@@ -5,25 +5,25 @@ import (
5 5
 	"strings"
6 6
 )
7 7
 
8
-func IPString(addr net.Addr) string {
8
+func IPString(addr net.Addr) Name {
9 9
 	addrStr := addr.String()
10 10
 	ipaddr, _, err := net.SplitHostPort(addrStr)
11 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 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 23
 	if err != nil {
24
-		return addr
24
+		return Name(addr)
25 25
 	}
26 26
 
27 27
 	hostname := strings.TrimSuffix(names[0], ".")
28
-	return hostname
28
+	return Name(hostname)
29 29
 }

+ 27
- 27
irc/reply.go Visa fil

@@ -79,23 +79,23 @@ func (target *Client) MultilineReply(names []string, code NumericCode, format st
79 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 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 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 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 99
 	return NewStringReply(client, PART, "%s :%s", channel, message)
100 100
 }
101 101
 
@@ -117,10 +117,10 @@ func RplPing(target Identifier) string {
117 117
 }
118 118
 
119 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 124
 	return NewStringReply(client, QUIT, ":%s", message)
125 125
 }
126 126
 
@@ -128,16 +128,16 @@ func RplError(message string) string {
128 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 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 136
 	return NewStringReply(client, KICK, "%s %s :%s",
137 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 141
 	return NewStringReply(client, KICK,
142 142
 		"%s :%s", target.Nick(), comment)
143 143
 }
@@ -184,7 +184,7 @@ func (target *Client) RplTopic(channel *Channel) {
184 184
 
185 185
 // <nick> <channel>
186 186
 // NB: correction in errata
187
-func (target *Client) RplInvitingMsg(invitee *Client, channel string) {
187
+func (target *Client) RplInvitingMsg(invitee *Client, channel Name) {
188 188
 	target.NumericReply(RPL_INVITING,
189 189
 		"%s %s", invitee.Nick(), channel)
190 190
 }
@@ -253,7 +253,7 @@ func (target *Client) RplWhoReply(channel *Channel, client *Client) {
253 253
 	}
254 254
 
255 255
 	if channel != nil {
256
-		channelName = channel.name
256
+		channelName = channel.name.String()
257 257
 		if target.capabilities[MultiPrefix] {
258 258
 			if channel.members[client][ChannelOperator] {
259 259
 				flags += "@"
@@ -275,12 +275,12 @@ func (target *Client) RplWhoReply(channel *Channel, client *Client) {
275 275
 }
276 276
 
277 277
 // <name> :End of WHO list
278
-func (target *Client) RplEndOfWho(name string) {
278
+func (target *Client) RplEndOfWho(name Name) {
279 279
 	target.NumericReply(RPL_ENDOFWHO,
280 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 284
 	switch mode {
285 285
 	case BanMask:
286 286
 		target.RplBanList(channel, mask)
@@ -306,7 +306,7 @@ func (target *Client) RplEndOfMaskList(mode ChannelMode, channel *Channel) {
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 310
 	target.NumericReply(RPL_BANLIST,
311 311
 		"%s %s", channel, mask)
312 312
 }
@@ -316,7 +316,7 @@ func (target *Client) RplEndOfBanList(channel *Channel) {
316 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 320
 	target.NumericReply(RPL_EXCEPTLIST,
321 321
 		"%s %s", channel, mask)
322 322
 }
@@ -326,7 +326,7 @@ func (target *Client) RplEndOfExceptList(channel *Channel) {
326 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 330
 	target.NumericReply(RPL_INVITELIST,
331 331
 		"%s %s", channel, mask)
332 332
 }
@@ -396,7 +396,7 @@ func (target *Client) RplVersion() {
396 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 400
 	target.NumericReply(RPL_INVITING,
401 401
 		"%s %s", invitee.Nick(), channel)
402 402
 }
@@ -412,7 +412,7 @@ func (target *Client) RplWhoWasUser(whoWas *WhoWas) {
412 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 416
 	target.NumericReply(RPL_ENDOFWHOWAS,
417 417
 		"%s :End of WHOWAS", nickname)
418 418
 }
@@ -426,7 +426,7 @@ func (target *Client) ErrAlreadyRegistered() {
426 426
 		":You may not reregister")
427 427
 }
428 428
 
429
-func (target *Client) ErrNickNameInUse(nick string) {
429
+func (target *Client) ErrNickNameInUse(nick Name) {
430 430
 	target.NumericReply(ERR_NICKNAMEINUSE,
431 431
 		"%s :Nickname is already in use", nick)
432 432
 }
@@ -441,12 +441,12 @@ func (target *Client) ErrUsersDontMatch() {
441 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 445
 	target.NumericReply(ERR_NEEDMOREPARAMS,
446 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 450
 	target.NumericReply(ERR_NOSUCHCHANNEL,
451 451
 		"%s :No such channel", channel)
452 452
 }
@@ -471,7 +471,7 @@ func (target *Client) ErrBadChannelKey(channel *Channel) {
471 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 475
 	target.NumericReply(ERR_NOSUCHNICK,
476 476
 		"%s :No such nick/channel", nick)
477 477
 }
@@ -493,7 +493,7 @@ func (target *Client) ErrRestricted() {
493 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 497
 	target.NumericReply(ERR_NOSUCHSERVER, "%s :No such server", server)
498 498
 }
499 499
 
@@ -521,7 +521,7 @@ func (target *Client) ErrNoNicknameGiven() {
521 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 525
 	target.NumericReply(ERR_ERRONEUSNICKNAME,
526 526
 		"%s :Erroneous nickname", nick)
527 527
 }
@@ -536,7 +536,7 @@ func (target *Client) ErrChannelIsFull(channel *Channel) {
536 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 540
 	target.NumericReply(ERR_WASNOSUCHNICK,
541 541
 		"%s :There was no such nickname", nickname)
542 542
 }

+ 23
- 21
irc/server.go Visa fil

@@ -24,9 +24,9 @@ type Server struct {
24 24
 	db        *sql.DB
25 25
 	idle      chan *Client
26 26
 	motdFile  string
27
-	name      string
27
+	name      Name
28 28
 	newConns  chan net.Conn
29
-	operators map[string][]byte
29
+	operators map[Name][]byte
30 30
 	password  []byte
31 31
 	signals   chan os.Signal
32 32
 	whoWas    *WhoWasList
@@ -41,7 +41,7 @@ func NewServer(config *Config) *Server {
41 41
 		db:        OpenDB(config.Server.Database),
42 42
 		idle:      make(chan *Client, 16),
43 43
 		motdFile:  config.Server.MOTD,
44
-		name:      config.Server.Name,
44
+		name:      NewName(config.Server.Name),
45 45
 		newConns:  make(chan net.Conn, 16),
46 46
 		operators: config.Operators(),
47 47
 		signals:   make(chan os.Signal, 1),
@@ -68,7 +68,7 @@ func loadChannelList(channel *Channel, list string, maskMode ChannelMode) {
68 68
 	if list == "" {
69 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 74
 func (server *Server) loadChannels() {
@@ -80,7 +80,9 @@ func (server *Server) loadChannels() {
80 80
 		log.Fatal("error loading channels: ", err)
81 81
 	}
82 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 86
 		var userLimit uint64
85 87
 		var banList, exceptList, inviteList string
86 88
 		err = rows.Scan(&name, &flags, &key, &topic, &userLimit, &banList,
@@ -248,15 +250,15 @@ func (server *Server) MOTD(client *Client) {
248 250
 	client.RplMOTDEnd()
249 251
 }
250 252
 
251
-func (s *Server) Id() string {
253
+func (s *Server) Id() Name {
252 254
 	return s.name
253 255
 }
254 256
 
255 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 262
 	return s.Id()
261 263
 }
262 264
 
@@ -339,7 +341,7 @@ func (m *NickCommand) HandleRegServer(s *Server) {
339 341
 		return
340 342
 	}
341 343
 
342
-	if !IsNickname(m.nickname) {
344
+	if !m.nickname.IsNickname() {
343 345
 		client.ErrErroneusNickname(m.nickname)
344 346
 		return
345 347
 	}
@@ -416,7 +418,7 @@ func (msg *NickCommand) HandleServer(server *Server) {
416 418
 		return
417 419
 	}
418 420
 
419
-	if !IsNickname(msg.nickname) {
421
+	if !msg.nickname.IsNickname() {
420 422
 		client.ErrErroneusNickname(msg.nickname)
421 423
 		return
422 424
 	}
@@ -447,13 +449,13 @@ func (m *JoinCommand) HandleServer(s *Server) {
447 449
 
448 450
 	if m.zero {
449 451
 		for channel := range client.channels {
450
-			channel.Part(client, client.Nick())
452
+			channel.Part(client, client.Nick().Text())
451 453
 		}
452 454
 		return
453 455
 	}
454 456
 
455 457
 	for name, key := range m.channels {
456
-		if !IsChannel(name) {
458
+		if !name.IsChannel() {
457 459
 			client.ErrNoSuchChannel(name)
458 460
 			continue
459 461
 		}
@@ -497,7 +499,7 @@ func (msg *TopicCommand) HandleServer(server *Server) {
497 499
 
498 500
 func (msg *PrivMsgCommand) HandleServer(server *Server) {
499 501
 	client := msg.Client()
500
-	if IsChannel(msg.target) {
502
+	if msg.target.IsChannel() {
501 503
 		channel := server.channels.Get(msg.target)
502 504
 		if channel == nil {
503 505
 			client.ErrNoSuchChannel(msg.target)
@@ -577,13 +579,13 @@ func (client *Client) WhoisChannelsNames() []string {
577 579
 	for channel := range client.channels {
578 580
 		switch {
579 581
 		case channel.members[client][ChannelOperator]:
580
-			chstrs[index] = "@" + channel.name
582
+			chstrs[index] = "@" + channel.name.String()
581 583
 
582 584
 		case channel.members[client][Voice]:
583
-			chstrs[index] = "+" + channel.name
585
+			chstrs[index] = "+" + channel.name.String()
584 586
 
585 587
 		default:
586
-			chstrs[index] = channel.name
588
+			chstrs[index] = channel.name.String()
587 589
 		}
588 590
 		index += 1
589 591
 	}
@@ -635,7 +637,7 @@ func (msg *WhoCommand) HandleServer(server *Server) {
635 637
 		for _, channel := range server.channels {
636 638
 			whoChannel(client, channel, friends)
637 639
 		}
638
-	} else if IsChannel(mask) {
640
+	} else if mask.IsChannel() {
639 641
 		// TODO implement wildcard matching
640 642
 		channel := server.channels.Get(mask)
641 643
 		if channel != nil {
@@ -685,7 +687,7 @@ func (msg *IsOnCommand) HandleServer(server *Server) {
685 687
 	ison := make([]string, 0)
686 688
 	for _, nick := range msg.nicks {
687 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,7 +700,7 @@ func (msg *MOTDCommand) HandleServer(server *Server) {
698 700
 
699 701
 func (msg *NoticeCommand) HandleServer(server *Server) {
700 702
 	client := msg.Client()
701
-	if IsChannel(msg.target) {
703
+	if msg.target.IsChannel() {
702 704
 		channel := server.channels.Get(msg.target)
703 705
 		if channel == nil {
704 706
 			client.ErrNoSuchChannel(msg.target)
@@ -785,7 +787,7 @@ func (msg *NamesCommand) HandleServer(server *Server) {
785 787
 }
786 788
 
787 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 793
 func (msg *DebugCommand) HandleServer(server *Server) {
@@ -880,7 +882,7 @@ func (msg *KillCommand) HandleServer(server *Server) {
880 882
 	}
881 883
 
882 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 888
 func (msg *WhoWasCommand) HandleServer(server *Server) {

+ 66
- 0
irc/strings.go Visa fil

@@ -0,0 +1,66 @@
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 Visa fil

@@ -67,7 +67,7 @@ type ReplyCode interface {
67 67
 	String() string
68 68
 }
69 69
 
70
-type StringCode string
70
+type StringCode Name
71 71
 
72 72
 func (code StringCode) String() string {
73 73
 	return string(code)
@@ -86,25 +86,25 @@ func (mode ChannelMode) String() string {
86 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 95
 func (channels ChannelNameMap) Add(channel *Channel) error {
96
-	if channels[channel.name] != nil {
96
+	if channels[channel.name.ToLower()] != nil {
97 97
 		return fmt.Errorf("%s: already set", channel.name)
98 98
 	}
99
-	channels[channel.name] = channel
99
+	channels[channel.name.ToLower()] = channel
100 100
 	return nil
101 101
 }
102 102
 
103 103
 func (channels ChannelNameMap) Remove(channel *Channel) error {
104
-	if channel != channels[channel.name] {
104
+	if channel != channels[channel.name.ToLower()] {
105 105
 		return fmt.Errorf("%s: mismatch", channel.name)
106 106
 	}
107
-	delete(channels, channel.name)
107
+	delete(channels, channel.name.ToLower())
108 108
 	return nil
109 109
 }
110 110
 
@@ -182,8 +182,8 @@ func (channels ChannelSet) First() *Channel {
182 182
 //
183 183
 
184 184
 type Identifier interface {
185
-	Id() string
186
-	Nick() string
185
+	Id() Name
186
+	Nick() Name
187 187
 }
188 188
 
189 189
 type Replier interface {

+ 5
- 5
irc/whowas.go Visa fil

@@ -7,10 +7,10 @@ type WhoWasList struct {
7 7
 }
8 8
 
9 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 16
 func NewWhoWasList(size uint) *WhoWasList {
@@ -32,7 +32,7 @@ func (list *WhoWasList) Append(client *Client) {
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 36
 	results := make([]*WhoWas, 0)
37 37
 	for whoWas := range list.Each() {
38 38
 		if nickname != whoWas.nickname {

Laddar…
Avbryt
Spara